commit 9481e88bbda1f1b61bee98f9f5300b22f7e4f453 Author: sam Date: Sun Sep 8 18:03:02 2024 +1200 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/.session.vim b/.session.vim new file mode 100644 index 0000000..ac1a512 --- /dev/null +++ b/.session.vim @@ -0,0 +1,54 @@ +let SessionLoad = 1 +let s:so_save = &g:so | let s:siso_save = &g:siso | setg so=0 siso=0 | setl so=-1 siso=-1 +let v:this_session=expand(":p") +silent only +silent tabonly +cd ~/Downloads/prboom/prboom-plus/prboom2 +if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == '' + let s:wipebuf = bufnr('%') +endif +let s:shortmess_save = &shortmess +if &shortmess =~ 'A' + set shortmess=aoOA +else + set shortmess=aoO +endif +badd +0 .gitignore +argglobal +%argdel +$argadd .gitignore +edit .gitignore +argglobal +setlocal fdm=manual +setlocal fde=0 +setlocal fmr={{{,}}} +setlocal fdi=# +setlocal fdl=0 +setlocal fml=1 +setlocal fdn=20 +setlocal fen +silent! normal! zE +let &fdl = &fdl +let s:l = 1 - ((0 * winheight(0) + 15) / 30) +if s:l < 1 | let s:l = 1 | endif +keepjumps exe s:l +normal! zt +keepjumps 1 +normal! 06| +tabnext 1 +if exists('s:wipebuf') && len(win_findbuf(s:wipebuf)) == 0 && getbufvar(s:wipebuf, '&buftype') isnot# 'terminal' + silent exe 'bwipe ' . s:wipebuf +endif +unlet! s:wipebuf +set winheight=1 winwidth=20 +let &shortmess = s:shortmess_save +let s:sx = expand(":p:r")."x.vim" +if filereadable(s:sx) + exe "source " . fnameescape(s:sx) +endif +let &g:so = s:so_save | let &g:siso = s:siso_save +set hlsearch +nohlsearch +doautoall SessionLoadPost +unlet SessionLoad +" vim: set ft=vim : diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..7839f8e --- /dev/null +++ b/AUTHORS @@ -0,0 +1,163 @@ +This file is now the amalgamated list of authors, contributors and credits +for PrBoom. Hopefully by keeping these all in one place, they will remain +more accurate. + +Doom was originally written by id software; when playing with any id main +wad file, you can see their list of credits, which includes the list of +programmers. After some years, they released the source code, to allow +others to work on improving the game. + +One of the first projects was DosDoom, by Chi Hoang. This was a quick port +of the released source code, which was for Linux, to DOS. This was then +picked up by TeamTNT (http://www.teamtnt.com/), who produced Boom, a greatly +debugged and extended version of Doom. The Boom programmers were Lee +Killough, Jim Flynn, Rand Phares, Ty Halderman. + +Several projects started working from the Boom source code. One was PrBoom, +made by Florian Schulze, that ported the code to Windows, added suport for +higher resolutions and later OpenGL. Another was Marine's Best Friend +(known as MBF) by Lee Killough, which fixed a lot of Boom bugs and added +many new game features. Finally, there was LxDoom, a port of Boom to Linux +by Colin Phipps. + +In October 1999, id Software re-released the Doom source code under the +GNU General Public License. TeamTNT have also changed to the new license, +and the other sources mentioned above have all allowed their code to be +GPLed. So PrBoom is covered by the GPL. + +In May 2000, LxDoom, PrBoom, and a derived port called LSDLDoom, merged into +one. The current authors of PrBoom are: + +Florian Schulze +Colin Phipps +Neil Stevens - Mac OS X porting +Andrey Budko +Rob Young (RjY) + +Our thanks go to all the authors of the ports mentioned above, and also the +following people who contributed code to LxDoom or PrBoom: + +Jess Haas +Of LSDLdoom, who merged his project into PrBoom, contributing his SDL code. + +Nicolas Kalkhof +Much work on the OpenGL renderer. + +James "Quasar" Haley +Ever willing to talk about source ideas, and has pointed me in the direction of +a lot of Boom and MBF bugs; also various bits code from his port Eternity have +been used, such as the BEX enhancements. + +Bob Aman (sporkmonger.com) +Created the RMUDAnsiTextView class used in the Mac launcher. + +Gady Kozma gady@math.tau.ac.il +Added hires to the SVGALib version of LxDoom, and other useful patches. + +Dick Leban +Lots of feedback about portability issues and helping get the network code +working properly back at v1.3.6. + +Eduardo Casino Almao +Lots of helpful feedback and suggestions, but more importantly actually getting +to grips with the code and giving very precise bug reports and patches. + +Joey Hess +For numerous patches, like the glibc fixes and window manager updates, and +help with the music. + +Ben Winslow +Various useful patches, like the colour ENDOOM code. + +Josh Parsons josh@schlick.anu.edu.au +Sent me the patches to use autoconf for configuring LxDoom. + +Steve Van Devender +Found the bug causing slight noise at the start of sounds playing, and other +patches. + +Barry Mead +Improvements to the mouse code and other odd patches. + +Mattias Kunkel +Made the lxdoom.spec file for creating LxDoom RPMs. + +Vicente Aguilar vicente@hal.dhis.org +Handy patch for the file handling + +Benjamin L McGee +Patch fixing the joystick code. + +Chris Young +Patch improving the ENDOOM printing + +Peter Jay Salzman +Cleanup patches + +Oliver Kraus +Send bug reports and patches for Solaris/Sparc. + +Paul S Jenner +Nice patch to make RPM building easier + +Julian +Fixed inline asm for gcc-2.95 (from Eternity) + +Lionel Ulmer +Patch to fix alignment problems on ARM processors. + +Ville Vuorinen +Spotted and helped patch the player spawn bug, as well as helping with some +Win32 issues. + +Steven Elliot +Misc patches. + +Andreas Dehmel +Spotted & patched a savegame bug. + +Jon Dowland +Bug reports & fixes, documentation improvements. + +If you have sent in patches and I forgot to list you, I apologise. Please email +me and I will add you. + +Also, thanks to the following people who have helped in various ways: + +Simon "fraggle" Howard +More MBF bugs. + +Robert Phipps +Network game testing, feature suggestions etc. + +Udo Monk +His port xdoom is very portable, and I referred to his code sometimes for help +with the X stuff; also his collection of Doom tools (XWadTools) is the +definitive tools collection for Linux. + +Andre Majorel +For Yadex, so I can debug those problematic levels more easily. + +Michael Heasley +Author of musserver, which helped me first add music support. + +Rafael Reilova +Helped with the music server program for LxDoom + +Frederic Oghdayan +For useful feedback on LxDoom v1.0.1, and repeating his bug reports until I +believed them :-). + +Adam Hegyi +Prompted me to hunt down those last few demo sync bugs, and provided some useful +insights and example demos to help. + +Adam Williamson +Pointing me toward yet another compatibility bug. + +Ingo van Lil +Another bug spotter. + +Everyone who contributed indirectly to MBF and Boom and Doom; see the +respective documentation files. + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..31e8798 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,243 @@ +cmake_minimum_required(VERSION 3.0) + +project("PrBoom-Plus" + VERSION 2.6.66 + HOMEPAGE_URL "https://github.com/coelckers/prboom-plus") + +# Set a default build type if none was specified +set(default_build_type "RelWithDebInfo") +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to '${default_build_type}' as none was specified.") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE + STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Release" "MinSizeRel" "RelWithDebInfo") +endif() + +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +include(TargetArch) + +include(TestBigEndian) +TEST_BIG_ENDIAN(WORDS_BIGENDIAN) + +# Check if a CMAKE_INSTALL_DOCDIR is provided before GNUInstallDirs sets its +# own default; this lets us set our own PrBoom-Plus default docdir later +# without clobbering a user-configured one +set(CUSTOM_DOCDIR "${CMAKE_INSTALL_DOCDIR}") +include(GNUInstallDirs) + +# Automated dependencies discovery, mostly needed for MSVC +target_architecture(TARGET_ARCH) +if(${TARGET_ARCH} MATCHES "i386") + set(DEPENDENCY_SUFFIX x86) +elseif(${TARGET_ARCH} MATCHES "x86_64") + set(DEPENDENCY_SUFFIX x64) +endif() +set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../dependencies_${DEPENDENCY_SUFFIX}") + +set(PACKAGE_NAME "${PROJECT_NAME}") +set(PACKAGE_TARNAME "prboom-plus") +set(PACKAGE_VERSION "${PROJECT_VERSION}") +set(PACKAGE_HOMEPAGE "${PROJECT_HOMEPAGE_URL}") +set(PACKAGE_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") +if(NOT CUSTOM_DOCDIR) + set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PACKAGE_TARNAME}" CACHE PATH "" FORCE) +endif() + +include(CheckSymbolExists) + +check_symbol_exists(stricmp "string.h" HAVE_STRICMP) +if(NOT HAVE_STRICMP) + add_definitions("-Dstricmp=strcasecmp") +endif() +check_symbol_exists(strnicmp "string.h" HAVE_STRNICMP) +if(NOT HAVE_STRNICMP) + add_definitions("-Dstrnicmp=strncasecmp") +endif() + +check_symbol_exists(getopt "unistd.h" HAVE_GETOPT) +check_symbol_exists(mmap "sys/mman.h" HAVE_MMAP) +check_symbol_exists(CreateFileMapping "windows.h" HAVE_CREATE_FILE_MAPPING) +if(NOT WIN32) + set(CMAKE_REQUIRED_DEFINITIONS_PREV ${CMAKE_REQUIRED_DEFINITIONS}) + set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_GNU_SOURCE) + check_symbol_exists(sched_setaffinity "sched.h" HAVE_SCHED_SETAFFINITY) + set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS_PREV}) + if(HAVE_SCHED_SETAFFINITY) + add_definitions(-D_GNU_SOURCE) + endif() +endif() +check_symbol_exists(usleep "unistd.h" HAVE_USLEEP) +check_symbol_exists(strsignal "string.h" HAVE_STRSIGNAL) +check_symbol_exists(mkstemp "stdlib.h" HAVE_MKSTEMP) + +include(CheckIncludeFile) + +check_include_file("sys/wait.h" HAVE_SYS_WAIT_H) +check_include_file("unistd.h" HAVE_UNISTD_H) +check_include_file("asm/byteorder.h" HAVE_ASM_BYTEORDER_H) +check_include_file("dirent.h" HAVE_DIRENT_H) + +option(BUILD_GL "Enable OpenGL rendering code" ON) +if(BUILD_GL) + set(OpenGL_GL_PREFERENCE LEGACY) + find_package(OpenGL) +endif() + +find_package(SDL2 2.0.7 REQUIRED) + +option(WITH_IMAGE "Use SDL2_image if available" ON) +if(WITH_IMAGE) + find_package(SDL2_image) + if(SDL2_IMAGE_FOUND) + set(HAVE_LIBSDL2_IMAGE TRUE) + endif() +endif() + +option(WITH_MIXER "Use SDL2_mixer if available" ON) +if(WITH_MIXER) + find_package(SDL2_mixer) + if(SDL2_MIXER_FOUND) + set(HAVE_LIBSDL2_MIXER TRUE) + endif() +endif() + +option(WITH_NET "Use SDL2_net if available" ON) +if(WITH_NET) + find_package(SDL2_net) + if(SDL2_NET_FOUND) + set(HAVE_NET TRUE) + set(USE_SDL_NET TRUE) + endif() +endif() + +option(WITH_PCRE "Use PCRE if available" ON) +if(WITH_PCRE) + find_package(PCREPOSIX) + if(PCREPOSIX_FOUND) + set(HAVE_LIBPCREPOSIX TRUE) + endif() +endif() + +option(WITH_ZLIB "Use ZLIB if available" ON) +if(WITH_ZLIB) + find_package(ZLIB) + if(ZLIB_FOUND) + set(HAVE_LIBZ TRUE) + endif() +endif() + +option(WITH_MAD "Use libmad if available" ON) +if(WITH_MAD) + find_package(LibMad) + if(LIBMAD_FOUND) + set(HAVE_LIBMAD TRUE) + endif() +endif() + +option(WITH_FLUIDSYNTH "Use FluidSynth if available" ON) +if(WITH_FLUIDSYNTH) + find_package(FluidSynth) + if(FLUIDSYNTH_FOUND) + set(HAVE_LIBFLUIDSYNTH TRUE) + endif() +endif() + +option(WITH_DUMB "Use DUMB if available" ON) +if(WITH_DUMB) + find_package(DUMB) + if(DUMB_FOUND) + set(HAVE_LIBDUMB TRUE) + endif() +endif() + +option(WITH_VORBISFILE "Use vorbisfile if available" ON) +if(WITH_VORBISFILE) + find_package(Vorbis) + if(VORBIS_FOUND) + set(HAVE_LIBVORBISFILE TRUE) + endif() +endif() + +option(WITH_PORTMIDI "Use PortMidi if available" ON) +if(WITH_PORTMIDI) + find_package(PortMidi) + if(PortMidi_FOUND) + set(HAVE_LIBPORTMIDI TRUE) + endif() +endif() + +option(WITH_ALSA "Use ALSA MIDI if available" ON) +if(WITH_ALSA) + find_package(ALSA) + if(ALSA_FOUND) + set(HAVE_ALSA TRUE) + endif() +endif() + +set(CMAKE_REQUIRED_INCLUDES_PREV ${CMAKE_REQUIRED_INCLUDES}) +set(CMAKE_REQUIRED_LIBRARIES_PREV ${CMAKE_REQUIRED_LIBRARIES}) +set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${SDL2_INCLUDE_DIRS}) +set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${SDL2_LIBRARIES}) +check_symbol_exists(SDL_JoystickGetAxis "SDL.h" HAVE_SDL_JOYSTICKGETAXIS) +set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES_PREV}) +set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES_PREV}) + +set(DOOMWADDIR "${CMAKE_INSTALL_PREFIX}/share/games/doom" CACHE PATH "Path to look for WAD files") +set(PRBOOMDATADIR "${CMAKE_INSTALL_PREFIX}/share/${PACKAGE_TARNAME}" CACHE PATH "Path to install supplemental files") + +option(SIMPLECHECKS "Enable checks which only impose significant overhead if a posible error is detected" ON) +option(ZONEIDCHECK "Enable id checks on zone blocks, to detect corrupted and illegally freed blocks" ON) + +# Debug options, disabled by default +option(RANGECHECK "Enable internal range checking" OFF) +option(INSTRUMENTED "Enable real-time memory allocation statistics, and extra debugging features" OFF) +option(TIMEDIAG "Enable creation of time stamps each time a lump is locked, and reporting of lumps locked for long periods of time" OFF) +option(HEAPCHECK "Turn on continuous heap checking (very slow)" OFF) +option(HEAPDUMP "Turn on dumping the heap state for debugging" OFF) + +configure_file(cmake/config.h.cin config.h) + +add_definitions(-DHAVE_CONFIG_H) + +if (MSVC) + add_definitions("/D_CRT_SECURE_NO_WARNINGS") +else() + set(CMAKE_C_FLAGS + "${CMAKE_C_FLAGS} \ + -Wall -Wno-missing-field-initializers -Wwrite-strings -Wundef \ + -Wbad-function-cast -Wcast-align -Wcast-qual -Wdeclaration-after-statement \ + -Wpointer-arith -Wno-unused -Wno-switch -Wno-sign-compare -Wno-pointer-sign \ + -ffast-math" + ) +endif() + +# Support cross compiling +option(FORCE_CROSSCOMPILE "Enable cross-compilation" OFF) +if(FORCE_CROSSCOMPILE) + set(CMAKE_CROSSCOMPILING ON) +endif() + +if(CMAKE_CROSSCOMPILING) + set(IMPORT_EXECUTABLES "IMPORTFILE-NOTFOUND" CACHE FILEPATH "Export file from native build") + include(${IMPORT_EXECUTABLES}) +else() + if(NOT CROSS_EXPORTS) + set(CROSS_EXPORTS "") + endif() +endif() + +set(PRBOOM_OUTPUT_PATH ${CMAKE_BINARY_DIR}) + +set(WAD_DATA prboom-plus.wad) +set(WAD_DATA_PATH "${PRBOOM_OUTPUT_PATH}/${WAD_DATA}") + +add_subdirectory(data) +add_subdirectory(doc) +add_subdirectory(src) + +if(NOT CMAKE_CROSSCOMPILING) + export(TARGETS ${CROSS_EXPORTS} FILE "${CMAKE_BINARY_DIR}/ImportExecutables.cmake") +endif() diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..f698bce --- /dev/null +++ b/COPYING @@ -0,0 +1,367 @@ +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 Library 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., 59 Temple Place - Suite 330, Boston, MA +02111-1307, 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) yyyy 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 Library General Public License instead of this +License. + + +---------------------------------------------------------------------- +---------- +Return to GNU's home page. +FSF & GNU inquiries & questions to gnu@gnu.org. Other ways to contact +the FSF. + +Comments on these web pages to webmasters@www.gnu.org, send other +questions to gnu@gnu.org. + +Copyright notice above. +Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, +MA 02111, USA + +Updated: 16 Feb 1998 tower + diff --git a/ICONS/icons.rc b/ICONS/icons.rc new file mode 100644 index 0000000..3ca1cd4 --- /dev/null +++ b/ICONS/icons.rc @@ -0,0 +1,65 @@ +#include "resource.h" +#include "winresrc.h" +#include "config.h" + +/* IDC_STATIC is documented in winuser.h, but not defined. */ +#ifndef IDC_STATIC +#define IDC_STATIC (-1) +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON DISCARDABLE "prboom-plus.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +#ifdef USE_WINDOWS_LAUNCHER + +#ifdef _MSC_VER +1 24 MOVEABLE PURE "prboom-plus.exe.manifest" +#else +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "prboom-plus.exe.manifest" +#endif + +IDD_LAUNCHERSERVERDIALOG DIALOGEX 0, 0, 187, 245 +STYLE DS_SYSMODAL | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | + WS_VISIBLE | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT | WS_EX_APPWINDOW +CAPTION "PrBoom-Plus Launcher" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,72,224,50,14 + PUSHBUTTON "Cancel",IDCANCEL,127,224,50,14 + CONTROL "Tab1",IDC_TAB1,"SysTabControl32",0x0,5,5,178,214 +END + +IDD_LAUNCHERCLIENTDIALOG DIALOGEX 7, 7, 173, 206 +STYLE DS_CONTROL | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "Game:",IDC_STATIC,4,5,22,8,0,WS_EX_TRANSPARENT + COMBOBOX IDC_IWADCOMBO,4,15,164,100,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + LTEXT "Files:",IDC_STATIC,4,32,17,8,0,WS_EX_TRANSPARENT + LISTBOX IDC_PWADLIST,4,42,164,110,LBS_SORT | + LBS_NOINTEGRALHEIGHT | LBS_MULTICOLUMN | LBS_EXTENDEDSEL | + WS_VSCROLL | WS_HSCROLL | WS_TABSTOP + LTEXT "History:",IDC_STATIC,4,155,38,8,0,WS_EX_TRANSPARENT + COMBOBOX IDC_HISTORYCOMBO,4,165,164,100,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Commands:",IDC_STATIC,4,181,38,8,0,WS_EX_TRANSPARENT + COMBOBOX IDC_COMMANDCOMBO,4,191,164,100,CBS_DROPDOWNLIST | + WS_VSCROLL + LTEXT "",IDC_FULLFILENAMESTATIC,23,32,142,8 +END + +#endif // USE_WINDOWS_LAUNCHER +///////////////////////////////////////////////////////////////////////////// diff --git a/ICONS/prboom-plus.bash b/ICONS/prboom-plus.bash new file mode 100644 index 0000000..e5449f7 --- /dev/null +++ b/ICONS/prboom-plus.bash @@ -0,0 +1,51 @@ +# bash completion for PrBoom+ -*- shell-script -*- + +_prboom_plus() +{ + local cur prev words cword + _init_completion || return + + # Save the previous switch on the command line in the prevsw variable + local i prevsw="" + for (( i=1; $cword > 1 && i <= $cword; i++ )); do + if [[ ${words[i]} == -* ]]; then + prevsw=${words[i]} + fi + done + + # Allow adding more than one file with the same extension to the same switch + case $prevsw in + -iwad|-file) + _filedir wad + ;; + -deh) + _filedir '@(bex|deh)' + ;; + -record*|-*demo) + _filedir lmp + ;; + esac + + if [[ $cur == -* ]]; then + COMPREPLY=( $( compgen -W '-1 -2 -3 -altdeath -aspect -auto -avg + -avidemo -bexout -blockmap -complevel -config -deathmatch -debugfile + -deh -devparm -fast -fastdemo -ffmap -file -fullscreen -geom -height + -iwad -levelstat -noaccel -noblit -nocheats -nodraw -nodrawers + -nofullscreen -nojoy -nomonsters -nomouse -nomusic -nosfx -nosound + -nowindow -playdemo -port -record -recordfromto -resetgamma -respawn + -save -shorttics -shotdir -skill -skipsec -solo-net -spechit -timedemo + -timer -viddump -videodriver -vidmode -viewangle -warp -width -window' \ + -- "$cur" ) ) + else + # DoLooseFiles() takes any file names on the command line before the + # first switch parm and inserts the appropriate -file, -deh or -playdemo + # switch in front of them. + if [[ -z "$prevsw" ]]; then + _filedir '@(bex|deh|lmp|wad)' + fi + fi +} && + +complete -F _prboom_plus prboom-plus + +# ex: ts=4 sw=4 et filetype=sh diff --git a/ICONS/prboom-plus.desktop b/ICONS/prboom-plus.desktop new file mode 100644 index 0000000..48f4169 --- /dev/null +++ b/ICONS/prboom-plus.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Type=Application +Name=PrBoom+ +Comment=enhanced clone of the classic first-person shooter Doom +Icon=prboom-plus +TryExec=prboom-plus +Exec=prboom-plus %F +Categories=Game;ActionGame; +MimeType=application/x-doom-wad; +Keywords=first;person;shooter;doom; diff --git a/ICONS/prboom-plus.exe.manifest b/ICONS/prboom-plus.exe.manifest new file mode 100644 index 0000000..a46fd6c --- /dev/null +++ b/ICONS/prboom-plus.exe.manifest @@ -0,0 +1,32 @@ + + + + +PrBoom-Plus + + + + + + + + + + + true + + + diff --git a/ICONS/prboom-plus.ico b/ICONS/prboom-plus.ico new file mode 100644 index 0000000..171db77 Binary files /dev/null and b/ICONS/prboom-plus.ico differ diff --git a/ICONS/prboom-plus.svg b/ICONS/prboom-plus.svg new file mode 100644 index 0000000..7298486 --- /dev/null +++ b/ICONS/prboom-plus.svg @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/ICONS/resource.h b/ICONS/resource.h new file mode 100644 index 0000000..0fd38ba --- /dev/null +++ b/ICONS/resource.h @@ -0,0 +1,20 @@ +#define IDI_ICON1 101 +#define IDI_ICON2 102 +#define IDI_ICON3 103 +#define IDI_ICON4 104 +#define IDI_ICON5 105 +#define IDI_ICON6 106 +#define IDD_LAUNCHERSERVERDIALOG 120 +#define IDD_LAUNCHERCLIENTDIALOG 121 +#define IDC_TAB1 1003 +#define IDC_COMBO1 1004 +#define IDC_IWADCOMBO 1004 +#define IDC_IWADLIST 1006 +#define IDC_LIST2 1006 +#define IDC_PWADLIST 1006 +#define IDC_LIST3 1007 +#define IDC_COMMANDCOMBO 1008 +#define IDC_HISTORYCOMBO 1009 +#define IDC_EDIT2 1014 +#define IDC_CHECK1 1015 +#define IDC_FULLFILENAMESTATIC 1016 diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..c73e604 --- /dev/null +++ b/INSTALL @@ -0,0 +1,245 @@ +Basic Installation (ZIP, tarball, Git) +================== + + 1. To compile PrBoom-Plus, you need at least SDL2. To enable various + extensions and optional features, the following are required: + - SDL2_net (for network play) + - SDL2_image (for high-res textures in OpenGL, and PNG screenshots) + - PCRE (for demo filename pattern matching) + - for music support, some or all of + * SDL2_mixer (midi, timidity) + * Fluidsynth (midi) + * ALSA (midi) + * Portmidi (midi) + * MAD (mp3) + * DUMB (various tracker-style formats) + * vorbisfile (ogg) + On a typical Debian or Ubuntu system this may be sufficient: + apt-get install libsdl2-dev libsdl2-net-dev libsdl2-image-dev \ + libpcre3-dev libsdl2-mixer-dev libfluidsynth-dev \ + libportmidi-dev libmad0-dev libdumb1-dev libvorbis-dev libasound2-dev + + You will also need `cmake` and `make` if you haven't already got them. + + 2. `cd' to the directory containing the PrBoom distribution (the directory + this file is in, prboom2). + + Create a new `build` folder and `cd` into it. + `mkdir build && cd build` + + Run `cmake .. -DCMAKE_BUILD_TYPE=Release` from this folder, or + `cmake .. -DCMAKE_BUILD_TYPE=Debug` for a debug build. (This gives + slightly lower performance, but more details in the event of + issues). + + This should end with the message "Build files have been written to: [folderpath]". + + 3. Type `make` to compile PrBoom. This may take some time; while it's + compiling I suggest you read the README, or maybe go and look for some + good doom levels to play when it's finished :-). + + This should work on all Linux systems, but systems that have a make + utility other than GNU make may have problems. E.g. you might need to + install GNU make, and then use it instead of make for these + instructions. I have tested BSD pmake and it works fine. + + 4. Type `make install/strip' as root, to install the programs, data files + and man pages for PrBoom. If you don't have root access on the machine, + you should ask the syadmin to do this and the next step for you. + + If you want to install manually then you should put the `prboom-plus.wad` + in /usr/local/share/games/doom/ (or symlink it from there). + + 5. Copy your Doom, Doom 2, Ultimate Doom or Final Doom IWAD (doom.wad or + doom2.wad) to /usr/local/share/games/doom/ (or symlink it from there). + Or if you don't have any of those, use the shareware IWAD, which you can + get from http://www.doomworld.com/ or http://www.idsoftware.com/. + + If you have a system with many users, you should read the license for + your version of Doom, and make sure only those users allowed to use it + can access the IWAD file. + + You may also aquire FreeDoom from https://freedoom.github.io/ which is a + free replacement to the iD Doom iWADs. + + 6. You can remove the program binaries and object files from the + source directory by typing `make clean`. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 7. PrBoom is now ready for use. If /usr/local/games is in your path, + you can just run prboom; otherwise give the full path. + + + +Building an RPM +=============== + +If you are on a system which uses the RPM packaging format, you might prefer +to build an RPM containing the PrBoom binaries rather than installing them +directly. Follow steps 1 and 2 above, then run a "make rpm". This performs +the usual proceedure for building the rpm in /usr/src/redhat/. Note that +the RPM sets its own parameters to ./configure; if you want to override +them you'll have to edit the build scripts yourself. Note that to get the +correct permissions, you either have to run this process as root, or use the +wrapper program fakeroot (version 0.4.5 or later - grab it from the Debian +source archive) and make sure you have permissions to the RPM build area. + +Installation Details +==================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.ac' is used to create `configure' by a program +called `autoconf'. You only need `configure.ac' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..25e92fe --- /dev/null +++ b/NEWS @@ -0,0 +1,300 @@ +PrBoom-Plus 2.6.66 @ 20 Jun 2023 +* Move default data directory to XDG_DATA_HOME on Linux by @facespkz in https://github.com/coelckers/prboom-plus/pull/470 +* sort resolutions by width first and height second by @fabiangreffrath in https://github.com/coelckers/prboom-plus/pull/472 +* check return status when checking for old prboom_dir by @facespkz in https://github.com/coelckers/prboom-plus/pull/473 +* Fix integer overflow during multiplication with realtic_clock_rate by @jengelh in https://github.com/coelckers/prboom-plus/pull/476 +* Fix UMAPINFO par times not loading for finale levels. by @JadingTsunami in https://github.com/coelckers/prboom-plus/pull/479 +* Don't overwrite user-configured custom docdir by @vilhelmgray in https://github.com/coelckers/prboom-plus/pull/480 +* Generate mixer buffer size from sample rate by @facespkz in https://github.com/coelckers/prboom-plus/pull/483 +* Allow uppercase episode shortcuts in UMAPINFO by @facespkz in https://github.com/coelckers/prboom-plus/pull/486 +* Add complevel hints for Default compatibility level by @facespkz in https://github.com/coelckers/prboom-plus/pull/488 +* UMAPINFO: Fix nextsecret inconsistencies by @rfomin in https://github.com/coelckers/prboom-plus/pull/497 +* Fix mid-texture judder when upper and middle textures overlap. by @JadingTsunami in https://github.com/coelckers/prboom-plus/pull/507 +* include wipe effect into video capture by @vadosnaprimer in https://github.com/coelckers/prboom-plus/pull/506 +* allow to -warp to MAP00 by @fabiangreffrath in https://github.com/coelckers/prboom-plus/pull/509 +* Workaround for optimization bug in clang by @rfomin in https://github.com/coelckers/prboom-plus/pull/512 +* Show a message when demo recording starts. by @JadingTsunami in https://github.com/coelckers/prboom-plus/pull/517 +* Remove shift when initializing translation tables vs. colormap. by @JadingTsunami in https://github.com/coelckers/prboom-plus/pull/519 +* Fix PortMidi level transitions by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/523 +* replace doomedsda.us links with dsdarchive.com by @andrey-budko in https://github.com/coelckers/prboom-plus/pull/525 +* Add proper SysEx support to PortMidi by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/526 +* Fix color translation tables to not rely on player map arrow colors. by @JadingTsunami in https://github.com/coelckers/prboom-plus/pull/527 +* Fix pitch bend typo for PortMidi by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/529 +* Add configurable reverb and chorus for PortMidi by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/530 +* Adjust music volume slider to match vanilla Doom by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/531 +* Use non-linear scaling for high volume levels by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/532 +* Adjust PortMidi volume slider to match midiOutSetVolume by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/533 +* Remove alpha channel from FBO scene texture. by @JadingTsunami in https://github.com/coelckers/prboom-plus/pull/534 +* UMAPINFO: bring par times fix in line with Woof by @fabiangreffrath in https://github.com/coelckers/prboom-plus/pull/537 +* Fixing wall texture bleed when a sector is inside the sky by @JadingTsunami in https://github.com/coelckers/prboom-plus/pull/542 +* Send all notes/sound off prior to SysEx reset by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/543 +* Fix MIDI looping when using reset delay by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/544 +* Clean up and fixes for PortMidi by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/548 +* Fix FluidSynth mode and polyphony by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/550 +* PortMidi: Use channel volume control, remove extra delay after reset by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/552 +* Replace win_fopen.c with wrappers used in Chocolate Doom by @rfomin in https://github.com/coelckers/prboom-plus/pull/554 +* PortMidi: Preserve timing when filtering SysEx; default to GM mode by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/555 +* PortMidi: Add fix for MS GS Synth bug by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/557 +* PortMidi: Fix songs with missing "hold pedal off" events by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/560 +* PortMidi: Send "reset all controllers" when song loops by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/562 +* Fix M_getcwd() in MinGW build by @rfomin in https://github.com/coelckers/prboom-plus/pull/563 +* Fix string overflow with large map numbers. by @JadingTsunami in https://github.com/coelckers/prboom-plus/pull/569 +* Supply correct texture format for GL wipe. by @JadingTsunami in https://github.com/coelckers/prboom-plus/pull/570 +* PortMidi: Update volume after "reset all controllers" event by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/571 +* PortMidi: Optimize reset messages by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/572 +* PortMidi Updates by @ceski-1 in https://github.com/coelckers/prboom-plus/pull/576 +* UMAPINFO: Introduce the new author field. by @rfomin in https://github.com/coelckers/prboom-plus/pull/436 + +PrBoom-Plus 2.6.2 @ 11 Feb 2022 +- added a prospective fix for OpenGL rendering: don't bleed walls through sky floors +- account for proper crosshair offsets when locked on to monsters +- relaxed "IWAD tag not present" error to a warning, which allows loading REKKR IWAD +- fixed processing of multiple DEHs +- UMAPINFO: fixed error reporting if token == TK_NoToken +- fixed intermission screen for E0Mx +- fixed Alt+Tabbing on Windows, broken in new SDL2 version +- UMAPINFO: fixed using_FMI reset * no more crashes at textscreens + (e.g. after MAP06, MAP11 or MAP20) after viewing finale picture upon finishing + a level with UMAPINFO 'endpic' property +- fixed freezing at TITLEPIC while using mousewheel in menus +- fixed looping forever in G_NextWeapon() +- UMAPINFO: fix 'entering' and 'enterpic' shown on exit levels +- GL: adjust sky offsets for non-standard FOVs +- fixed stuttering with uncapped framerate +- added support for widescreen low resolutions +- added REKKR to launcher string list +- let G_GotoNextLevel() close the circle for maps with an endpic +- Launcher: identify IWADs either by IWAD tag, or by PWAD tag and matching filename +- Portmidi: fix freeze after song change +- fixed scaled time interpolation when changing game speed +- fixed wrong pitch of SFX when MIDI Player is set to SDL +- v1.2 compat: + * do not use sfx_getpow for powerups + * do not render powerups and attacking Spiderdemon fullbright +- UMAPINFO: fixed par times not showing +- fixed demo playback warp consistency +- added SKILL cheat to display current skill level +- fixed calculation of interpolation frac value +- guard against screen buffer underflow +- fixed crash at startup by properly closing IWAD file handle +- added vanilla_keymap option à la Chocolate Doom +- added 'run' as a mouse-settable control (mouseb_speed) + +PrBoom-Plus 2.6.1um @ 16 Aug 2021 +- fixed MOD playback with DUMB 2.x +- added TNTCLxx cheat, e.g. TNTCL04 sets complevel 4 (Final DOOM) +- draw crosshair before messages, draw crosshair for fist/chainsaw and during on-screen messages +- fixed crosshair locking on target for melee weapons +- more consistently check for ammo instead of weapon type +- fixed odd strafe values (vanilla allows even values only) +- added TNTSHORT cheat to toggle shorttics on and off while in game +- fixed typo in ZDoom-style Actor name (Slalagtite -> Stalagtite) +- fixed Windows Alt-Tab +- fixed a memory leak on SDL_Quit() +- preserve automap position/scale when toggling overlay mode +- allow binding weapon selection to mouse buttons +- add a `-pistolstart` command-line parameter +- fixed crashes on WADs with empty music lumps, e.g. Nihility.wad +- enabled loading 16 bit RIFF wavs in WADs +- make low-pass sound filter (introduced by the previous change) optional +- if executable directory appears not writable, fall back to `.` +- made pitch-shift range independent of target samplerate +- fixed bogus condition preventing automap lines from being drawn +- fixed saving of mouse button bindings +- fixed interpolated z-coordinate after teleporting +- added a new option "Disable sound cutoffs" to allow removed map objects to finish playing their sounds and to make seesounds uninterruptible +- allow deleting savegames from menu +- fixed automap crash when `USE_VERTEX_ARRAYS` and `GL_VBO` are undefined +- fluidsynth: reset all controllers when looping a song +- fixed hanging notes on exit when using Portmidi +- show current and next levels when typing IDCLEV cheat +- added a config key for "exclusive" i.e. mode-changing fullscreen +- UMAPINFO: if interbackdrop does not specify a valid flat, draw it as a patch instead +- appended revision tracker to UMAPINFO spec document +- ALSA sequencer API backend for ALSA MIDI support (Linux only) +- extended range of maps reachable via `-warp` and IDCLEV: + * from E1M1-E4M9 to E1M0-E9M99 for D1 + * from MAP01-MAP33 to MAP00-MAP99 + * Limitations: + * E0My doesn't work, since episode must always be > 0 + * `-warp x 0`, because zero passed to `-warp` as map number is used to warping to first changed map in PWAD +- evaluate mouse motion only once per tick +- show Time/STS widgets above status bar +- removed lowpass-filter item from menu +- fixed `P_SetMobjState()` stack, fixes crash on mayhem18p.wad/m18p31-1509.lmp +- fixed crash in automap on certain maps (e.g. Sunlust MAP01) when using GL renderer, Thing Appearance = Icons and 2xIDDT +- fixed menu intermission desync +- fixed crash when leaving menu in Chex Quest +- initialize the map title widget with the generic map lump name +- make "Next Level" key simply increase map number if already outside regular level transition range +- check for next level in IDCLEV cheat before announcing it +- make sure "Next Level" key never warps to a non-existent map +- reformatted umapinfo.txt so that it is easier to read in plaintext +- fixed custom episode select for Doom 2 +- fixed shifting automap markers +- allow translucent sprites on all complevels +- allow disabling predefined translucency on all complevels +- fixed flood HOM on certain maps (e.g. Plutonia MAP11, HR MAP01) (GL) +- fixed sky walls for cases with sky vs. sky toptextures (e.g. E4M6) (GL) +- fixed transparent spites being rendered incorrectly with transparent walls in view (GL) +- generate a default save-slot name when saving to an empty slot +- added mouse button binds for turn left/right +- fixed sky scaling for non-standard sky sizes, e.g. Eviternity and Ancient Aliens (GL) +- added new advanced HUD layout by Fireb3rt+ +- added slice_samplecount config option +- added manifest to declare app as DPI aware, so that Windows will not apply DPI virtualization +- added support for widescreen assets +- added ZDoom's names for the new Crispy Doom/DEHEXTRA things to ActorNames[], so they can be used with BossAction() if needed +- fixed UMAPINFO music restarting issue +- skip the 'entering level' screen if one of endgame, endpic, endbunny or endcast is set (UMAPINFO) +- fixed endbunny and endgame UMAPINFO fields +- reset mlook on demo recording +- added OpenGL sprite fuzz options ("darken", "shadow", "transparent", "ghostly") +- properly check for the CreateFileMapping() symbol (build) +- always precache sound lumps +- replace W_SafeGetNumForName() with W_CheckNumForName() +- removed defunct lump locking warnings +- removed obsolete SFX usefulness references +- allow colored blood to get set/overridden by DEHACKED, implemented Eternity's coloredblood spec, added three additional color translation tables for black, purple and white +- made the Ouch Face fix available on all complevels +- added the autoload directory feature from Chocolate Doom +- implemented custom atexit() sequence, ported over from Chocolate Doom, fixing crashes during shutdown e.g. on Linux using Wayland +- fixed switching weapons when reaching zero ammo with boom_autoswitch == 0 +- made Doom sound quirk fixes available on all complevels +- UMAPINFO: introduced the new 'label' field +- do not cast degenmobj_t as mobj_t, breaks aliasing rules on platforms where mobj_t pointers are 64-bit aligned and +degenmobj_t pointers are only 32-bit aligned +- fixed `-solo-net` for demo restart +- fixed levelstat crash for MAP33 and above +- added autoload per PWAD feature, further enhancing the autoload directory feature +- UMAPINFO: fixed 'episode' field parsing, 'label' is single-line +- removed obsolete weapon toggle setting +- added ftell() and fseek() implementations for DEHACKED lumps, allowing the DEH parser to roll back to the start of a line following a blank line and re-process it with the last valid block code handler +- added mouse look / vertical mouse movement toggle notifications +- fixed forcing aspect ratio correction only for the canonical 16:10 (i.e. 320x200 and 640x400) modes +- do not even attempt to play DEMO4 if it is not available +- only apply palette changes when inside a level +- fixed 32-bit widescreen pain palette +- added a `-coop_spawns` parameter for using "coop in single-player" mode +- fixed OpenGL effect palettes +- added support for non-latin paths and filenames on Windows +- made sure to always autoload prboom-plus.wad +- introduced a supplemental data directory "PRBOOMDATADIR" to install prboom-plus.wad into (build) +- prepared for different autoload base directories, respecting the `-noload` parameter +- fixed playback of complevel 11 demo with UMAPINFO +- fixed attempted playback of a UMAPINFO demo without UMAPINFO loaded +- never exceed desktop resolution in fullscreen desktop mode +- UMAPINFO: entries without defined 'levelname' fall back to default +- UMAPINFO: fixed default intermission +- fixed setting 'finaleflat' in FMI_StartFinale (UMAPINFO) +- fixed load game during multiplayer demo playback +- fixed OpenGL backfill + +PrBoom-Plus 2.6um @ 09 Feb 2021 +- fixed episode selection when playing Doom 2 +- fixed Doom 1 level progression +- print a blinking arrow next to the currently highlighted menu item +- print another blinking arrow to the right of the currently selected menu setting +- support the DOOMWADPATH environment variable +- add some extra states, sprites and mobjtypes from Doom Retro and Crispy Doom +- fullscreen desktop for the software renderer +- improved v1.2 compat: + - fixed a desync on the internal demo in DTH1.WAD + - fixed the walkways to the BFG rising a little too high on 1_ON_1.WAD + - moving floors (except plats) should not make the floor stop sound +- fixed infinite loop in macOS launcher +- added CMake build system +- fixed the mainfest to allow 64 bit builds +- fixed PortMidi support on 64-bit Windows +- disabled the "popping skull player death" easter egg +- do not wait for VSync if running a timedemo +- added -statdump parameter from Chocolate Doom +- added mouse-strafe divisor setting +- removed and/or replace all non-free data lumps +- improved Boom random seed +- added -stroller param +- include secret exit in -levelstat +- added configurable quickstart window +- disallow 180-turn with strafe on +- fixed endianess for 32-bit ZDoom nodes +- show Extended Help screen when pressing the Help key +- made armor color depend on type, not amount +- added Chocolate Doom mouse behavior option +- fixed Boom autoswitch behavior +- reinstated the "WEAPON ATTACK ALIGNMENT" menu option +- changed "SECRET AREAS" menu option to "REPORT REVEALED SECRETS" +- fixed endianess for DeePBSP V4 nodes +- show error message boxes on Windows, except when video-dumping a demo +- unbind game speed changing keys in default config +- fixed heap buffer overflows in UDP multiplayer code +- fixed -longtics having no effect +- changed video capture to an ffmpeg-based approach +- track demo joins for TAS-detection purposes +- fixed some episode transition issues when using UMAPINFO +- added automap rotate and overlay keybindings to menus +- fixed a bug in DeHackEd string replacements with a length of exactly four +- added "Dropped Item" menu support +- enabled automap Follow Mode by default +- disabled chorus and reverb for Fluidsynth by default +- fix segfault with -viewangle parameter +- next levels no longer reset if endscreen is disabled in UMAPINFO +- added 200 Sounds for DEHEXTRA +- respawn when using IDDQD while dead +- fixes for empty DeHackEd lumps and entries +- restrict "next level" button usage to in-level only +- added adaptations for fluidsynth 2.0 +- added "No Vertical Mouse" setting and keybind +- save / load -complevel 9 friction +- added extensible demo format + "PR+UM" signature +- made canonical resolutions (320x200, 320x240, 640x400 and 640x480) + always available in menu, regardless of what video driver/SDL report +- fixed aspect ratio for canonical resolutions; be sure to set "Aspect Ratio" to "Auto" + and "Status Bar and Menu Appearance" to "Not Adjusted" for both 320x200 and 640x400 +- set minimum windows size to prevent window from shrinking when changing video modes +- "Screen Multiple Factor" now functions like a "window size factor" + (i.e. like "-1", "-2", "-3" in Chocolate Doom) and has no effect in fullscreen modes +- added "Integer Screen Scaling" option (à la Crispy Doom), which restricts the screen + size to integer multiples of the game resolution, under Options -> General, page 5 +- both executables now share the same config file "prboom-plus.cfg", with all config + keys always preserved - including the GL-related ones +- switched to the unified executable "prboom-plus" with optional OpenGL rendering +- portmidi: pitch bends are now reset when stopping song +- added support for more mouse buttons +- fixed resolution-independent mouse sensitivity +- added demo restart functionality - record a new demo on map restart +- setup menus remember last active item +- fixed the KFA cheat sting not read from .bex patches +- UMAPINFO docs updated re: "bossaction" field +- fixed a crash when running out of space while typing in a file path into the autoload + fields (Options -> General, page 2) +- fixed the "Garbage lines at the top of weapon sprites" issue (#95) +- add support for named complevels on command line: + -complevel 1.9 = -complevel 2 + -complevel doom2 = -complevel 2 + -complevel ultimate = -complevel 3 + -complevel udoom = -complevel 3 + -complevel final = -complevel 4 + -complevel tnt = -complevel 4 + -complevel plutonia = -complevel 4 + -complevel boom = -complevel 9 + -complevel mbf = -complevel 11 + -complevel vanilla = complevel autodetected according to IWAD loaded +- allow MBF sky transfers in all complevels +- add support for colored blood and gibs +- fixed key-bindings unusable after mouse-wheel up/down + +PrBoom-Plus 2.5.1.7um @ 16 Jun 2019 +- adds a special marker to demos recorded with UMAPINFO +- addes support for ZDBSP's compressed node format +- fixes the rendering problems in OpenGL +- fixes TRANMAP caching + +PrBoom-Plus 2.5.1.6um @ 11 Jun 2019 +This fixes a major bug with the episode selection menu not working and adds IDCLEV checks for maps outside the standard range. + +PrBoom-Plus 2.5.1.5um @ 10 Jun 2019 +First official release with UMAPINFO support + +2.5.1.4.umapinfo_test @ 22 Apr 2017 +Beta version with UMAPINFO support. +Note that this is for testing as the newly added MAPINFO feature has not seen thorough testing yet. diff --git a/README b/README new file mode 100644 index 0000000..d62e797 --- /dev/null +++ b/README @@ -0,0 +1,251 @@ +PrBoom 2.6.66 + +PrBoom is a version of the classic 3D shoot'em'up game Doom, originally +written by id Software. + +See the file AUTHORS in this distribution for a list of authors and +other contributors, and a history of the projects PrBoom is derived +from. + +PrBoom is made available under the GNU General Public License. See the +file COPYING included in this distribution for details. + +Please see the NEWS file included for changes since the previous version. + +Game data - WADs +---------------- + +(This section is aimed at people not familiar with Doom and the +data files it uses.) + +PrBoom is a game engine - it provides a program to play Doom levels, but +it doesn't include any levels itself. More importantly, you need all the +sounds, sprites, and other graphics that make up the Doom environment. +So to play PrBoom, you need one of the main Doom date files from id +Software - either doom.wad, doom2.wad, tnt.wad or plutonia.wad from one +of the commercial Doom games, or the shareware doom1.wad. This file +is called the IWAD. + +PrBoom also supports playing Doom add-on levels, called "PWADs", which +are small extra .wad files which just contain extra levels or other +resources. PWADs are ONLY ADD-ONS, you still need the original IWAD +that they are designed to work with. In practice, most PWADs on the +Internet require doom2.wad (although some work with doom.wad). + +If you don't own any of the Doom games, get the shareware doom1.wad +from doom19s.zip on Doomworld's shareware download page. But note that you +will not be able to play most add-ons. +http://www.doomworld.com/files/shareware.shtml + +Windows Installation +-------------------- + +Just extract the zip to a directory of your choice and copy your IWAD +files into it. Now you can make shortcuts and add "-iwad filename.wad" +to them. + +The SDL_mixer library used by PrBoom supports software MIDI music +synthesis. If you want to hear the Doom music, you need a set of +Timidity instrument patches. Do a web search for timidity patch sets, +there are plenty around. +These patch sets are a large download (>5megs). +SDL_mixer does not currently support hardware MIDI synthesis. But we +have added a hacked version of SDL_mixer with native midi support. If +you like to try it out, rename SDL_mixer_beta.dll to SDL_mixer.dll. +You should rename the original SDL_mixer.dll before. Tell us if it +works or not. Please note, that there might be bugs in the native midi +implementation. + +Linux Installation +------------------ + +For UNIX, Linux, and other POSIX systems, you need the SDL libraries in +order to use PrBoom. If you haven't already done so, visit +http://prboom.sourceforge.net/linux.html and follow the instructions there +for your system, downloading the necessary libraries, and either +installing the binary RPM package or compiling PrBoom from source. + +Once you've done that, you'll need to copy your IWAD file (see the section +above if you don't know what this is) to a directory where PrBoom can find +it. Make /usr/local/share/games/doom/, and copy your IWAD (all of your +IWADs, if you own more than one) to that directory. + +Mac OS Installation +------------------- + +Copy your IWAD (see above) into your home folder under +Library:Application Support:PrBoom (this folder will be created for you the +first time you run PrBoom). + +First Use +--------- + +If it's the first time you've run PrBoom, you'll need to do some configuring +to get the controls and display right for you. + +On a new installation, PrBoom runs at 640x480 resolution. If you have used +PrBoom before, you may have an old config file in your home directory which +specifies a lower resolution, such as Doom's normal 320x200. You can use the +-width and -height parameters to select a higher resolution, e.g.: + +prboom -width 640 -height 400 + +sets the resolution. This setting is remembered for future sessions. For +other parameters, see the included README.command-line. + +You may also wish to customise the key bindings. PrBoom's default keybindings +are the same as the original Doom; unlike original Doom, you can change key +bindings in the game. In the in-game menus, go to Options, Settings, Key +Bindings. + +On Mac OS X, you can't use the command line, but after running the program +once, you can edit YOURHOME:Library:Application Support:PrBoom:prboom.cfg to +change settings like your screen resolution. + +Features +-------- + + This is all the features PrBoom has compared to the original Doom game + - it's intended to give you an idea of the enhancements, rather than + burying you in details. + + See http://prboom.sourceforge.net/about.html for an HTML version of + this list. + + This is shamelessly modelled on Boom's changes pages. By each + change, there's the name of the port the feature comes from (so it's + compatible with). + +Playing the game + + * Supports loading dehacked files at the command line, or in WADs + (BOOM, MBF) + * Supports PWADs containing sprites and flats (BOOM) + * Save games and demos completely store game parameters (BOOM, + MBF) + * Savegames store list of loaded WAD files, warning if wrong files + loaded (BOOM, MBF) + +Game engine + + * Player bobbing improved, optional (BOOM, MBF) + * Friction effects (BOOM), affecting players and monsters + (MBF) + * Wind, current, conveyor effects (BOOM) + * Far more flexible scrolling wall/floor types (BOOM) + * Always run (BOOM) + * Weapon change logic overhauled and improved (BOOM) + * Support for friendly monsters, helper dogs (MBF) + * Monster target finding code improved (MBF) + * AI improvements (MBF) + * Bouncy and touchy things (MBF) + * New code pointers (MBF) + * Per-level and animated skies (MBF) + * Generalised line types system gives complete flexibility + (BOOM) + * Elevators (BOOM) + * Translucent sprites, walls (BOOM) + * Independent floor and ceiling lighting (BOOM) + * Silent teleports (BOOM) + * Deep water, true underwater areas (BOOM) + * Icon of Sin telefragging made more consistent (MBF) + * Fix large numbers of game bugs (BOOM, MBF, LxDoom) + * Support arbitrary texture heights (BOOM) + +Screen + + * High resolution support (PrBoom) + * Optional message console, multiple message lines (BOOM) + * Status bar shows health/armour/ammo in colours (BOOM) + * Heads up display, showing ammo, health, keys overlayed on view + (BOOM) + +Multiplayer + + * Spy mode improved (BOOM) + * Support for loadgame in a net game (LxDoom) + * Client server style network games (LxDoom) + +Automap + + * No limit on marks (BOOM) + * Rotation and overlay modes (DOSDoom, LxDoom) + * Map shows coordinates (BOOM), optionally follow pointer + (MBF) + * Teleport lines, key doors and switches marked specially (BOOM) + * Keys, secrets visible on map with cheat codes (BOOM) + * Colours fully configurable from menus (BOOM) + +Intermission screens + + * Par times hidden when not relevant (BOOM) + * Total episode time shown (LxDoom) + +Menus + + * F1 help screen shows current key setup (BOOM) + * Key bindings, monster behaviour, and compatibility settings all set + in menus (BOOM, MBF) + +Compatibility + + * Game is capable of behaving like any of: original Doom v1.9, Boom + v2.02, MBF (BOOM, MBF, LxDoom) + * Plays most original Doom v1.9 demos (more than Boom or MBF) + * Plays most Boom v2.02 demos (apart from levels with friction + effects everything should work). + * Plays some DOSDoom, earlier Doom, earlier Boom, and LxDoom demos. + * Plays all MBF demos. + * Auto-correction of common bugs in old levels (MBF), with + warnings (LxDoom) + * Fine control of options controlling compatibility and new features + (MBF) + +Controls + + * Greater control of key bindings from in game menus (BOOM) + * More accurate mouse sensitivity control (BOOM, LxDoom) + +Misc + + * Screenshot code improved, supports BMPs (BOOM) + * Support for ENDOOM and ENDBOOM (BOOM, LxDoom) + * -timedemo and -fastdemo options (BOOM) + * Real time frame rate, segs, visplanes, sprites display + (LxDoom) + * Various extra cheat codes (BOOM, LxDoom) + +Internals + + * Greatly improved internal memory management (BOOM, LxDoom) + * Startup time greatly shortened by lazy generation of some lookups + (DOSDoom, LxDoom) + * Removed internal limits (BOOM) + +Other Tips +---------- + +On Linux, SDL tries to detect an appropriate video device automatically. +If you want to overrite the default, you can set the SDL_VIDEODRIVER +enviromental variable. At a bash prompt, this is as easy as running: + +SDL_VIDEODRIVER=fbcon prboom +or +SDL_VIDEODRIVER=svga prboom + +Details +------- + +Details on these extra features are split into separate text files: + +README.demos provides a guide to PrBoom's demo support +README.compatibility describes PrBoom's various compatibility + options and modes +README.command-line gives a command line reference for prboom, + prboom-game-server, and the format of boom.cfg. + On UNIX/Linux systems use the man pages instead. + +Editing features are not covered in the docs with this package. We plan +to bundle the editing docs as a separate download. Watch our website +for news. + diff --git a/cmake/FindALSA.cmake b/cmake/FindALSA.cmake new file mode 100644 index 0000000..23003b3 --- /dev/null +++ b/cmake/FindALSA.cmake @@ -0,0 +1,69 @@ +# Alsa check, based on libkmid/configure.in.in. +# Only the support for Alsa >= 0.9.x was included; 0.5.x was dropped (but feel free to re-add it if you need it) +# It defines ... +# It offers the following macros: +# ALSA_CONFIGURE_FILE(config_header) - generate a config.h, typical usage: +# ALSA_CONFIGURE_FILE(${CMAKE_BINARY_DIR}/config-alsa.h) +# ALSA_VERSION_STRING(version_string) looks for alsa/version.h and reads the version string into +# the first argument passed to the macro + +# Copyright (c) 2006, David Faure, +# Copyright (c) 2007, Matthias Kretz +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +include(CheckIncludeFiles) +include(CheckIncludeFileCXX) +include(CheckLibraryExists) + +# Already done by toplevel +find_library(ASOUND_LIBRARY asound) +set(ASOUND_LIBRARY_DIR "") +if(ASOUND_LIBRARY) + get_filename_component(ASOUND_LIBRARY_DIR ${ASOUND_LIBRARY} PATH) +endif(ASOUND_LIBRARY) + +check_library_exists(asound snd_seq_create_simple_port "${ASOUND_LIBRARY_DIR}" HAVE_LIBASOUND2) +if(HAVE_LIBASOUND2) + message(STATUS "Found ALSA: ${ASOUND_LIBRARY}") +else(HAVE_LIBASOUND2) + message(STATUS "ALSA not found") +endif(HAVE_LIBASOUND2) +set(ALSA_FOUND ${HAVE_LIBASOUND2}) + +find_path(ALSA_INCLUDES alsa/version.h) + +macro(ALSA_VERSION_STRING _result) + # check for version in alsa/version.h + if(ALSA_INCLUDES) + file(READ "${ALSA_INCLUDES}/alsa/version.h" _ALSA_VERSION_CONTENT) + string(REGEX REPLACE ".*SND_LIB_VERSION_STR.*\"(.*)\".*" "\\1" ${_result} ${_ALSA_VERSION_CONTENT}) + else(ALSA_INCLUDES) + message(STATUS "ALSA version not known. ALSA output will probably not work correctly.") + endif(ALSA_INCLUDES) +endmacro(ALSA_VERSION_STRING _result) + + +get_filename_component(_FIND_ALSA_MODULE_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) +macro(ALSA_CONFIGURE_FILE _destFile) + check_include_files(sys/soundcard.h HAVE_SYS_SOUNDCARD_H) + check_include_files(machine/soundcard.h HAVE_MACHINE_SOUNDCARD_H) + + check_include_files(linux/awe_voice.h HAVE_LINUX_AWE_VOICE_H) + check_include_files(awe_voice.h HAVE_AWE_VOICE_H) + check_include_files(/usr/src/sys/i386/isa/sound/awe_voice.h HAVE__USR_SRC_SYS_I386_ISA_SOUND_AWE_VOICE_H) + check_include_files(/usr/src/sys/gnu/i386/isa/sound/awe_voice.h HAVE__USR_SRC_SYS_GNU_I386_ISA_SOUND_AWE_VOICE_H) + + check_include_file_cxx(sys/asoundlib.h HAVE_SYS_ASOUNDLIB_H) + check_include_file_cxx(alsa/asoundlib.h HAVE_ALSA_ASOUNDLIB_H) + + check_library_exists(asound snd_pcm_resume "${ASOUND_LIBRARY_DIR}" ASOUND_HAS_SND_PCM_RESUME) + if(ASOUND_HAS_SND_PCM_RESUME) + set(HAVE_SND_PCM_RESUME 1) + endif(ASOUND_HAS_SND_PCM_RESUME) + + configure_file(${_FIND_ALSA_MODULE_DIR}/config-alsa.h.cmake ${_destFile}) +endmacro(ALSA_CONFIGURE_FILE _destFile) + +mark_as_advanced(ALSA_INCLUDES ASOUND_LIBRARY) diff --git a/cmake/FindDUMB.cmake b/cmake/FindDUMB.cmake new file mode 100644 index 0000000..a8a5e1b --- /dev/null +++ b/cmake/FindDUMB.cmake @@ -0,0 +1,28 @@ + +SET(DUMB_SEARCH_PATHS + /usr/local/ + /usr + /opt +) + +FIND_PATH(DUMB_INCLUDE_DIR dumb.h + HINTS ${DUMB_ROOT} + PATH_SUFFIXES include include/dumb + PATHS ${DUMB_SEARCH_PATHS} +) +FIND_LIBRARY(DUMB_LIBRARY dumb + HINTS ${DUMB_ROOT} + PATH_SUFFIXES lib64 lib + PATHS ${DUMB_SEARCH_PATHS} +) + +IF(DUMB_INCLUDE_DIR AND DUMB_LIBRARY) + SET(DUMB_FOUND TRUE) +ENDIF (DUMB_INCLUDE_DIR AND DUMB_LIBRARY) + + +IF(DUMB_FOUND) + MESSAGE(STATUS "Found Dumb: ${DUMB_LIBRARY}") +ELSE(DUMB_FOUND) + MESSAGE(STATUS "Could NOT find Dumb") +ENDIF(DUMB_FOUND) diff --git a/cmake/FindFluidSynth.cmake b/cmake/FindFluidSynth.cmake new file mode 100644 index 0000000..7d5cb6a --- /dev/null +++ b/cmake/FindFluidSynth.cmake @@ -0,0 +1,23 @@ +# - Find fluidsynth +# Find the native fluidsynth includes and library +# +# FLUIDSYNTH_INCLUDE_DIR - where to find fluidsynth.h +# FLUIDSYNTH_LIBRARIES - List of libraries when using fluidsynth. +# FLUIDSYNTH_FOUND - True if fluidsynth found. + + +IF (FLUIDSYNTH_INCLUDE_DIR AND FLUIDSYNTH_LIBRARIES) + # Already in cache, be silent + SET(FluidSynth_FIND_QUIETLY TRUE) +ENDIF (FLUIDSYNTH_INCLUDE_DIR AND FLUIDSYNTH_LIBRARIES) + +FIND_PATH(FLUIDSYNTH_INCLUDE_DIR fluidsynth.h) + +FIND_LIBRARY(FLUIDSYNTH_LIBRARIES NAMES fluidsynth ) +MARK_AS_ADVANCED( FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR ) + +# handle the QUIETLY and REQUIRED arguments and set FLUIDSYNTH_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(FluidSynth DEFAULT_MSG FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR) + diff --git a/cmake/FindLibMad.cmake b/cmake/FindLibMad.cmake new file mode 100644 index 0000000..e20fb0c --- /dev/null +++ b/cmake/FindLibMad.cmake @@ -0,0 +1,45 @@ +#------------------------------------------------------------------------------- +# +# Copyright 2013-2018 BBC Research and Development +# +# This file is part of Audio Waveform Image Generator. +# +# Author: Chris Needham +# +# Audio Waveform Image Generator is free software: you can redistribute it +# and/or modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the License, +# or (at your option) any later version. +# +# Audio Waveform Image Generator is distributed in the hope that it will be +# useful, but WITHOUT 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 +# Audio Waveform Image Generator. If not, see . +# +#------------------------------------------------------------------------------- +# +# Finds libmad include file and library. This module sets the following +# variables: +# +# LIBMAD_FOUND - Flag if libmad was found +# LIBMAD_INCLUDE_DIRS - libmad include directories +# LIBMAD_LIBRARIES - libmad library paths +# +#------------------------------------------------------------------------------- + +include(FindPackageHandleStandardArgs) + +find_path(LIBMAD_INCLUDE_DIRS mad.h) +find_library(LIBMAD_LIBRARIES mad) + +find_package_handle_standard_args( + LibMad + DEFAULT_MSG + LIBMAD_LIBRARIES + LIBMAD_INCLUDE_DIRS +) + +#------------------------------------------------------------------------------- diff --git a/cmake/FindOgg.cmake b/cmake/FindOgg.cmake new file mode 100644 index 0000000..fda5893 --- /dev/null +++ b/cmake/FindOgg.cmake @@ -0,0 +1,53 @@ +# - Find ogg library +# Find the native Ogg headers and libraries. +# +# OGG_INCLUDE_DIRS - where to find ogg/ogg.h, etc. +# OGG_LIBRARIES - List of libraries when using ogg. +# OGG_FOUND - True if ogg is found. + +#============================================================================= +#Copyright 2000-2009 Kitware, Inc., Insight Software Consortium +#All rights reserved. +# +#Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +#* Redistributions of source code must retain the above copyright notice, +#this list of conditions and the following disclaimer. +# +#* Redistributions in binary form must reproduce the above copyright notice, +#this list of conditions and the following disclaimer in the documentation +#and/or other materials provided with the distribution. +# +#* Neither the names of Kitware, Inc., the Insight Software Consortium, nor +#the names of their contributors may be used to endorse or promote products +#derived from this software without specific prior written permission. +# +#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +#AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +#IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +#ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +#LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +#CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +#SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +#INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +#CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +#ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +#POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +# Look for the header file. +FIND_PATH(OGG_INCLUDE_DIR NAMES ogg/ogg.h) +MARK_AS_ADVANCED(OGG_INCLUDE_DIR) + +# Look for the library. +FIND_LIBRARY(OGG_LIBRARY NAMES ogg) +MARK_AS_ADVANCED(OGG_LIBRARY) + +# handle the QUIETLY and REQUIRED arguments and set OGG_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Ogg DEFAULT_MSG OGG_LIBRARY OGG_INCLUDE_DIR) + +SET(OGG_LIBRARIES ${OGG_LIBRARY}) +SET(OGG_INCLUDE_DIRS ${OGG_INCLUDE_DIR}) diff --git a/cmake/FindPCREPOSIX.cmake b/cmake/FindPCREPOSIX.cmake new file mode 100644 index 0000000..7cc40ec --- /dev/null +++ b/cmake/FindPCREPOSIX.cmake @@ -0,0 +1,34 @@ +# - Find pcreposix +# Find the native PCRE and PCREPOSIX include and libraries +# +# PCRE_INCLUDE_DIR - where to find pcreposix.h, etc. +# PCREPOSIX_LIBRARIES - List of libraries when using libpcreposix. +# PCRE_LIBRARIES - List of libraries when using libpcre. +# PCREPOSIX_FOUND - True if libpcreposix found. +# PCRE_FOUND - True if libpcre found. + +IF (PCRE_INCLUDE_DIR) + # Already in cache, be silent + SET(PCRE_FIND_QUIETLY TRUE) +ENDIF (PCRE_INCLUDE_DIR) + +FIND_PATH(PCRE_INCLUDE_DIR pcreposix.h) +FIND_LIBRARY(PCREPOSIX_LIBRARY NAMES pcreposix libpcreposix) +FIND_LIBRARY(PCRE_LIBRARY NAMES pcre libpcre) + +# handle the QUIETLY and REQUIRED arguments and set PCREPOSIX_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCREPOSIX DEFAULT_MSG PCREPOSIX_LIBRARY PCRE_INCLUDE_DIR) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE DEFAULT_MSG PCRE_LIBRARY) + +IF(PCREPOSIX_FOUND) + SET(PCREPOSIX_LIBRARIES ${PCREPOSIX_LIBRARY}) + SET(HAVE_LIBPCREPOSIX 1) + SET(HAVE_PCREPOSIX_H 1) +ENDIF(PCREPOSIX_FOUND) + +IF(PCRE_FOUND) + SET(PCRE_LIBRARIES ${PCRE_LIBRARY}) + SET(HAVE_LIBPCRE 1) +ENDIF(PCRE_FOUND) diff --git a/cmake/FindPortMidi.cmake b/cmake/FindPortMidi.cmake new file mode 100644 index 0000000..55a7485 --- /dev/null +++ b/cmake/FindPortMidi.cmake @@ -0,0 +1,36 @@ +# - Try to find PortMidi +# Once done, this will define +# +# PortMidi_FOUND - system has PortMidi +# PortMidi_INCLUDE_DIRS - the PortMidi include directories +# PortMidi_LIBRARIES - link these to use PortMidi +# PortMidi_VERSION - detected version of PortMidi +# +# See documentation on how to write CMake scripts at +# http://www.cmake.org/Wiki/CMake:How_To_Find_Libraries + +find_library(PORTMIDI_LIBRARY portmidi + HINTS + $ENV{PORTMIDI_DIR} +) + +find_library(PORTTIME_LIBRARY porttime + HINTS + $ENV{PORTMIDI_DIR} +) + +find_path(PORTMIDI_INCLUDE_DIR portmidi.h + HINTS + $ENV{PORTMIDI_DIR} +) + +set(PORTMIDI_LIBRARIES ${PORTMIDI_LIBRARY}) + +if (PORTTIME_LIBRARY) + list(APPEND PORTMIDI_LIBRARIES ${PORTTIME_LIBRARY}) +endif (PORTTIME_LIBRARY) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(PortMidi REQUIRED_VARS PORTMIDI_LIBRARIES PORTMIDI_INCLUDE_DIR) + +mark_as_advanced(PORTMIDI_LIBRARY) diff --git a/cmake/FindSDL2.cmake b/cmake/FindSDL2.cmake new file mode 100644 index 0000000..40178ca --- /dev/null +++ b/cmake/FindSDL2.cmake @@ -0,0 +1,368 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# Copyright 2019 Amine Ben Hassouna +# Copyright 2000-2019 Kitware, Inc. and Contributors +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. + +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. + +# * Neither the name of Kitware, Inc. nor the names of Contributors +# may be used to endorse or promote products derived from this +# software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[=======================================================================[.rst: +FindSDL2 +-------- + +Locate SDL2 library + +This module defines the following 'IMPORTED' targets: + +:: + + SDL2::Core + The SDL2 library, if found. + Libraries should link to SDL2::Core + + SDL2::Main + The SDL2main library, if found. + Applications should link to SDL2::Main instead of SDL2::Core + + + +This module will set the following variables in your project: + +:: + + SDL2_LIBRARIES, the name of the library to link against + SDL2_INCLUDE_DIRS, where to find SDL.h + SDL2_FOUND, if false, do not try to link to SDL2 + SDL2MAIN_FOUND, if false, do not try to link to SDL2main + SDL2_VERSION_STRING, human-readable string containing the version of SDL2 + + + +This module responds to the following cache variables: + +:: + + SDL2_PATH + Set a custom SDL2 Library path (default: empty) + + SDL2_NO_DEFAULT_PATH + Disable search SDL2 Library in default path. + If SDL2_PATH (default: ON) + Else (default: OFF) + + SDL2_INCLUDE_DIR + SDL2 headers path. + + SDL2_LIBRARY + SDL2 Library (.dll, .so, .a, etc) path. + + SDL2MAIN_LIBRAY + SDL2main Library (.a) path. + + SDL2_BUILDING_LIBRARY + This flag is useful only when linking to SDL2_LIBRARIES insead of + SDL2::Main. It is required only when building a library that links to + SDL2_LIBRARIES, because only applications need main() (No need to also + link to SDL2main). + If this flag is defined, then no SDL2main will be added to SDL2_LIBRARIES + and no SDL2::Main target will be created. + + +Don't forget to include SDLmain.h and SDLmain.m in your project for the +OS X framework based version. (Other versions link to -lSDL2main which +this module will try to find on your behalf.) Also for OS X, this +module will automatically add the -framework Cocoa on your behalf. + + +Additional Note: If you see an empty SDL2_LIBRARY in your project +configuration, it means CMake did not find your SDL2 library +(SDL2.dll, libsdl2.so, SDL2.framework, etc). Set SDL2_LIBRARY to point +to your SDL2 library, and configure again. Similarly, if you see an +empty SDL2MAIN_LIBRARY, you should set this value as appropriate. These +values are used to generate the final SDL2_LIBRARIES variable and the +SDL2::Core and SDL2::Main targets, but when these values are unset, +SDL2_LIBRARIES, SDL2::Core and SDL2::Main does not get created. + + +$SDL2DIR is an environment variable that would correspond to the +./configure --prefix=$SDL2DIR used in building SDL2. l.e.galup 9-20-02 + + + +Created by Amine Ben Hassouna: + Adapt FindSDL.cmake to SDL2 (FindSDL2.cmake). + Add cache variables for more flexibility: + SDL2_PATH, SDL2_NO_DEFAULT_PATH (for details, see doc above). + Mark 'Threads' as a required dependency for non-OSX systems. + Modernize the FindSDL2.cmake module by creating specific targets: + SDL2::Core and SDL2::Main (for details, see doc above). + + +Original FindSDL.cmake module: + Modified by Eric Wing. Added code to assist with automated building + by using environmental variables and providing a more + controlled/consistent search behavior. Added new modifications to + recognize OS X frameworks and additional Unix paths (FreeBSD, etc). + Also corrected the header search path to follow "proper" SDL + guidelines. Added a search for SDLmain which is needed by some + platforms. Added a search for threads which is needed by some + platforms. Added needed compile switches for MinGW. + +On OSX, this will prefer the Framework version (if found) over others. +People will have to manually change the cache value of SDL2_LIBRARY to +override this selection or set the SDL2_PATH variable or the CMake +environment CMAKE_INCLUDE_PATH to modify the search paths. + +Note that the header path has changed from SDL/SDL.h to just SDL.h +This needed to change because "proper" SDL convention is #include +"SDL.h", not . This is done for portability reasons +because not all systems place things in SDL/ (see FreeBSD). +#]=======================================================================] + +# Define options for searching SDL2 Library in a custom path + +set(SDL2_PATH "" CACHE STRING "Custom SDL2 Library path") + +set(_SDL2_NO_DEFAULT_PATH OFF) +if(SDL2_PATH) + set(_SDL2_NO_DEFAULT_PATH ON) +endif() + +set(SDL2_NO_DEFAULT_PATH ${_SDL2_NO_DEFAULT_PATH} + CACHE BOOL "Disable search SDL2 Library in default path") +unset(_SDL2_NO_DEFAULT_PATH) + +set(SDL2_NO_DEFAULT_PATH_CMD) +if(SDL2_NO_DEFAULT_PATH) + set(SDL2_NO_DEFAULT_PATH_CMD NO_DEFAULT_PATH) +endif() + +# Search for the SDL2 include directory +find_path(SDL2_INCLUDE_DIR SDL.h + HINTS + ENV SDL2DIR + ${SDL2_NO_DEFAULT_PATH_CMD} + PATH_SUFFIXES SDL2 + # path suffixes to search inside ENV{SDL2DIR} + include/SDL2 include + PATHS ${SDL2_PATH} + DOC "Where the SDL2 headers can be found" +) + +set(SDL2_INCLUDE_DIRS "${SDL2_INCLUDE_DIR}") + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(VC_LIB_PATH_SUFFIX lib/x64) +else() + set(VC_LIB_PATH_SUFFIX lib/x86) +endif() + +# SDL-2.0 is the name used by FreeBSD ports... +# don't confuse it for the version number. +find_library(SDL2_LIBRARY + NAMES SDL2 SDL-2.0 + HINTS + ENV SDL2DIR + ${SDL2_NO_DEFAULT_PATH_CMD} + PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX} + PATHS ${SDL2_PATH} + DOC "Where the SDL2 Library can be found" +) + +set(SDL2_LIBRARIES "${SDL2_LIBRARY}") + +if(NOT SDL2_BUILDING_LIBRARY) + if(NOT SDL2_INCLUDE_DIR MATCHES ".framework") + # Non-OS X framework versions expect you to also dynamically link to + # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms + # seem to provide SDL2main for compatibility even though they don't + # necessarily need it. + + if(SDL2_PATH) + set(SDL2MAIN_LIBRARY_PATHS "${SDL2_PATH}") + endif() + + if(NOT SDL2_NO_DEFAULT_PATH) + set(SDL2MAIN_LIBRARY_PATHS + /sw + /opt/local + /opt/csw + /opt + "${SDL2MAIN_LIBRARY_PATHS}" + ) + endif() + + find_library(SDL2MAIN_LIBRARY + NAMES SDL2main + HINTS + ENV SDL2DIR + ${SDL2_NO_DEFAULT_PATH_CMD} + PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX} + PATHS ${SDL2MAIN_LIBRARY_PATHS} + DOC "Where the SDL2main library can be found" + ) + unset(SDL2MAIN_LIBRARY_PATHS) + endif() +endif() + +# MinGW needs an additional link flag, -mwindows +# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -mwindows +if(MINGW) + set(MINGW32_LIBRARY mingw32 "-mwindows" CACHE STRING "link flags for MinGW") +endif() + +if(SDL2_LIBRARY) + # For SDL2main + if(SDL2MAIN_LIBRARY AND NOT SDL2_BUILDING_LIBRARY) + list(FIND SDL2_LIBRARIES "${SDL2MAIN_LIBRARY}" _SDL2_MAIN_INDEX) + if(_SDL2_MAIN_INDEX EQUAL -1) + set(SDL2_LIBRARIES "${SDL2MAIN_LIBRARY}" ${SDL2_LIBRARIES}) + endif() + unset(_SDL2_MAIN_INDEX) + endif() + + # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. + # CMake doesn't display the -framework Cocoa string in the UI even + # though it actually is there if I modify a pre-used variable. + # I think it has something to do with the CACHE STRING. + # So I use a temporary variable until the end so I can set the + # "real" variable in one-shot. + if(APPLE) + set(SDL2_LIBRARIES ${SDL2_LIBRARIES} "-framework Cocoa") + endif() + + # For threads, as mentioned Apple doesn't need this. + # In fact, there seems to be a problem if I used the Threads package + # and try using this line, so I'm just skipping it entirely for OS X. + if(NOT APPLE) + set(SDL2_LIBRARIES ${SDL2_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + endif() + + # For MinGW library + if(MINGW) + set(SDL2_LIBRARIES ${MINGW32_LIBRARY} ${SDL2_LIBRARIES}) + endif() + +endif() + +# Read SDL2 version +if(SDL2_INCLUDE_DIR AND EXISTS "${SDL2_INCLUDE_DIR}/SDL_version.h") + file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+[0-9]+$") + file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_MINOR_VERSION[ \t]+[0-9]+$") + file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_PATCHLEVEL[ \t]+[0-9]+$") + string(REGEX REPLACE "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MAJOR "${SDL2_VERSION_MAJOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MINOR "${SDL2_VERSION_MINOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_PATCH "${SDL2_VERSION_PATCH_LINE}") + set(SDL2_VERSION_STRING ${SDL2_VERSION_MAJOR}.${SDL2_VERSION_MINOR}.${SDL2_VERSION_PATCH}) + unset(SDL2_VERSION_MAJOR_LINE) + unset(SDL2_VERSION_MINOR_LINE) + unset(SDL2_VERSION_PATCH_LINE) + unset(SDL2_VERSION_MAJOR) + unset(SDL2_VERSION_MINOR) + unset(SDL2_VERSION_PATCH) +endif() + +include(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 + REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR + VERSION_VAR SDL2_VERSION_STRING) + +if(SDL2MAIN_LIBRARY) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2main + REQUIRED_VARS SDL2MAIN_LIBRARY SDL2_INCLUDE_DIR + VERSION_VAR SDL2_VERSION_STRING) +endif() + + +mark_as_advanced(SDL2_PATH + SDL2_NO_DEFAULT_PATH + SDL2_LIBRARY + SDL2MAIN_LIBRARY + SDL2_INCLUDE_DIR + SDL2_BUILDING_LIBRARY) + + +# SDL2:: targets (SDL2::Core and SDL2::Main) +if(SDL2_FOUND) + + # SDL2::Core target + if(SDL2_LIBRARY AND NOT TARGET SDL2::Core) + add_library(SDL2::Core UNKNOWN IMPORTED) + set_target_properties(SDL2::Core PROPERTIES + IMPORTED_LOCATION "${SDL2_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIR}") + + if(APPLE) + # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. + # For more details, please see above. + set_property(TARGET SDL2::Core APPEND PROPERTY + INTERFACE_LINK_OPTIONS "-framework Cocoa") + else() + # For threads, as mentioned Apple doesn't need this. + # For more details, please see above. + set_property(TARGET SDL2::Core APPEND PROPERTY + INTERFACE_LINK_LIBRARIES Threads::Threads) + endif() + endif() + + # SDL2::Main target + # Applications should link to SDL2::Main instead of SDL2::Core + # For more details, please see above. + if(NOT SDL2_BUILDING_LIBRARY AND NOT TARGET SDL2::Main) + + if(SDL2_INCLUDE_DIR MATCHES ".framework" OR NOT SDL2MAIN_LIBRARY) + add_library(SDL2::Main INTERFACE IMPORTED) + set_property(TARGET SDL2::Main PROPERTY + INTERFACE_LINK_LIBRARIES SDL2::Core) + elseif(SDL2MAIN_LIBRARY) + # MinGW requires that the mingw32 library is specified before the + # libSDL2main.a static library when linking. + # The SDL2::MainInternal target is used internally to make sure that + # CMake respects this condition. + add_library(SDL2::MainInternal UNKNOWN IMPORTED) + set_property(TARGET SDL2::MainInternal PROPERTY + IMPORTED_LOCATION "${SDL2MAIN_LIBRARY}") + set_property(TARGET SDL2::MainInternal PROPERTY + INTERFACE_LINK_LIBRARIES SDL2::Core) + + add_library(SDL2::Main INTERFACE IMPORTED) + + if(MINGW) + # MinGW needs an additional link flag '-mwindows' and link to mingw32 + set_property(TARGET SDL2::Main PROPERTY + INTERFACE_LINK_LIBRARIES "mingw32" "-mwindows") + endif() + + set_property(TARGET SDL2::Main APPEND PROPERTY + INTERFACE_LINK_LIBRARIES SDL2::MainInternal) + endif() + + endif() +endif() diff --git a/cmake/FindSDL2_image.cmake b/cmake/FindSDL2_image.cmake new file mode 100644 index 0000000..624e915 --- /dev/null +++ b/cmake/FindSDL2_image.cmake @@ -0,0 +1,222 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# Copyright 2019 Amine Ben Hassouna +# Copyright 2000-2019 Kitware, Inc. and Contributors +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. + +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. + +# * Neither the name of Kitware, Inc. nor the names of Contributors +# may be used to endorse or promote products derived from this +# software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[=======================================================================[.rst: +FindSDL2_image +-------------- + +Locate SDL2_image library + +This module defines the following 'IMPORTED' target: + +:: + + SDL2::Image + The SDL2_image library, if found. + Have SDL2::Core as a link dependency. + + + +This module will set the following variables in your project: + +:: + + SDL2_IMAGE_LIBRARIES, the name of the library to link against + SDL2_IMAGE_INCLUDE_DIRS, where to find the headers + SDL2_IMAGE_FOUND, if false, do not try to link against + SDL2_IMAGE_VERSION_STRING - human-readable string containing the + version of SDL2_image + + + +This module responds to the following cache variables: + +:: + + SDL2_IMAGE_PATH + Set a custom SDL2_image Library path (default: empty) + + SDL2_IMAGE_NO_DEFAULT_PATH + Disable search SDL2_image Library in default path. + If SDL2_IMAGE_PATH (default: ON) + Else (default: OFF) + + SDL2_IMAGE_INCLUDE_DIR + SDL2_image headers path. + + SDL2_IMAGE_LIBRARY + SDL2_image Library (.dll, .so, .a, etc) path. + + +Additional Note: If you see an empty SDL2_IMAGE_LIBRARY in your project +configuration, it means CMake did not find your SDL2_image library +(SDL2_image.dll, libsdl2_image.so, etc). Set SDL2_IMAGE_LIBRARY to point +to your SDL2_image library, and configure again. This value is used to +generate the final SDL2_IMAGE_LIBRARIES variable and the SDL2::Image target, +but when this value is unset, SDL2_IMAGE_LIBRARIES and SDL2::Image does not +get created. + + +$SDL2IMAGEDIR is an environment variable that would correspond to the +./configure --prefix=$SDL2IMAGEDIR used in building SDL2_image. + +$SDL2DIR is an environment variable that would correspond to the +./configure --prefix=$SDL2DIR used in building SDL2. + + + +Created by Amine Ben Hassouna: + Adapt FindSDL_image.cmake to SDL2_image (FindSDL2_image.cmake). + Add cache variables for more flexibility: + SDL2_IMAGE_PATH, SDL2_IMAGE_NO_DEFAULT_PATH (for details, see doc above). + Add SDL2 as a required dependency. + Modernize the FindSDL2_image.cmake module by creating a specific target: + SDL2::Image (for details, see doc above). + +Original FindSDL_image.cmake module: + Created by Eric Wing. This was influenced by the FindSDL.cmake + module, but with modifications to recognize OS X frameworks and + additional Unix paths (FreeBSD, etc). +#]=======================================================================] + +# SDL2 Library required +find_package(SDL2 QUIET) +if(NOT SDL2_FOUND) + set(SDL2_IMAGE_SDL2_NOT_FOUND "Could NOT find SDL2 (SDL2 is required by SDL2_image).") + if(SDL2_image_FIND_REQUIRED) + message(FATAL_ERROR ${SDL2_IMAGE_SDL2_NOT_FOUND}) + else() + if(NOT SDL2_image_FIND_QUIETLY) + message(STATUS ${SDL2_IMAGE_SDL2_NOT_FOUND}) + endif() + return() + endif() + unset(SDL2_IMAGE_SDL2_NOT_FOUND) +endif() + + +# Define options for searching SDL2_image Library in a custom path + +set(SDL2_IMAGE_PATH "" CACHE STRING "Custom SDL2_image Library path") + +set(_SDL2_IMAGE_NO_DEFAULT_PATH OFF) +if(SDL2_IMAGE_PATH) + set(_SDL2_IMAGE_NO_DEFAULT_PATH ON) +endif() + +set(SDL2_IMAGE_NO_DEFAULT_PATH ${_SDL2_IMAGE_NO_DEFAULT_PATH} + CACHE BOOL "Disable search SDL2_image Library in default path") +unset(_SDL2_IMAGE_NO_DEFAULT_PATH) + +set(SDL2_IMAGE_NO_DEFAULT_PATH_CMD) +if(SDL2_IMAGE_NO_DEFAULT_PATH) + set(SDL2_IMAGE_NO_DEFAULT_PATH_CMD NO_DEFAULT_PATH) +endif() + +# Search for the SDL2_image include directory +find_path(SDL2_IMAGE_INCLUDE_DIR SDL_image.h + HINTS + ENV SDL2IMAGEDIR + ENV SDL2DIR + ${SDL2_IMAGE_NO_DEFAULT_PATH_CMD} + PATH_SUFFIXES SDL2 + # path suffixes to search inside ENV{SDL2DIR} + # and ENV{SDL2IMAGEDIR} + include/SDL2 include + PATHS ${SDL2_IMAGE_PATH} + DOC "Where the SDL2_image headers can be found" +) + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(VC_LIB_PATH_SUFFIX lib/x64) +else() + set(VC_LIB_PATH_SUFFIX lib/x86) +endif() + +# Search for the SDL2_image library +find_library(SDL2_IMAGE_LIBRARY + NAMES SDL2_image + HINTS + ENV SDL2IMAGEDIR + ENV SDL2DIR + ${SDL2_IMAGE_NO_DEFAULT_PATH_CMD} + PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX} + PATHS ${SDL2_IMAGE_PATH} + DOC "Where the SDL2_image Library can be found" +) + +# Read SDL2_image version +if(SDL2_IMAGE_INCLUDE_DIR AND EXISTS "${SDL2_IMAGE_INCLUDE_DIR}/SDL_image.h") + file(STRINGS "${SDL2_IMAGE_INCLUDE_DIR}/SDL_image.h" SDL2_IMAGE_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_IMAGE_MAJOR_VERSION[ \t]+[0-9]+$") + file(STRINGS "${SDL2_IMAGE_INCLUDE_DIR}/SDL_image.h" SDL2_IMAGE_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_IMAGE_MINOR_VERSION[ \t]+[0-9]+$") + file(STRINGS "${SDL2_IMAGE_INCLUDE_DIR}/SDL_image.h" SDL2_IMAGE_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_IMAGE_PATCHLEVEL[ \t]+[0-9]+$") + string(REGEX REPLACE "^#define[ \t]+SDL_IMAGE_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_IMAGE_VERSION_MAJOR "${SDL2_IMAGE_VERSION_MAJOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL_IMAGE_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_IMAGE_VERSION_MINOR "${SDL2_IMAGE_VERSION_MINOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL_IMAGE_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_IMAGE_VERSION_PATCH "${SDL2_IMAGE_VERSION_PATCH_LINE}") + set(SDL2_IMAGE_VERSION_STRING ${SDL2_IMAGE_VERSION_MAJOR}.${SDL2_IMAGE_VERSION_MINOR}.${SDL2_IMAGE_VERSION_PATCH}) + unset(SDL2_IMAGE_VERSION_MAJOR_LINE) + unset(SDL2_IMAGE_VERSION_MINOR_LINE) + unset(SDL2_IMAGE_VERSION_PATCH_LINE) + unset(SDL2_IMAGE_VERSION_MAJOR) + unset(SDL2_IMAGE_VERSION_MINOR) + unset(SDL2_IMAGE_VERSION_PATCH) +endif() + +set(SDL2_IMAGE_LIBRARIES ${SDL2_IMAGE_LIBRARY}) +set(SDL2_IMAGE_INCLUDE_DIRS ${SDL2_IMAGE_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2_image + REQUIRED_VARS SDL2_IMAGE_LIBRARIES SDL2_IMAGE_INCLUDE_DIRS + VERSION_VAR SDL2_IMAGE_VERSION_STRING) + + +mark_as_advanced(SDL2_IMAGE_PATH + SDL2_IMAGE_NO_DEFAULT_PATH + SDL2_IMAGE_LIBRARY + SDL2_IMAGE_INCLUDE_DIR) + + +if(SDL2_IMAGE_FOUND) + + # SDL2::Image target + if(SDL2_IMAGE_LIBRARY AND NOT TARGET SDL2::Image) + add_library(SDL2::Image UNKNOWN IMPORTED) + set_target_properties(SDL2::Image PROPERTIES + IMPORTED_LOCATION "${SDL2_IMAGE_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${SDL2_IMAGE_INCLUDE_DIR}" + INTERFACE_LINK_LIBRARIES SDL2::Core) + endif() +endif() diff --git a/cmake/FindSDL2_mixer.cmake b/cmake/FindSDL2_mixer.cmake new file mode 100644 index 0000000..a71f26a --- /dev/null +++ b/cmake/FindSDL2_mixer.cmake @@ -0,0 +1,220 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# Copyright 2019 Amine Ben Hassouna +# Copyright 2000-2019 Kitware, Inc. and Contributors +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. + +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. + +# * Neither the name of Kitware, Inc. nor the names of Contributors +# may be used to endorse or promote products derived from this +# software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[=======================================================================[.rst: +FindSDL2_mixer +-------------- + +Locate SDL2_mixer library + +This module defines the following 'IMPORTED' target: + +:: + + SDL2::Mixer + The SDL2_mixer library, if found. + Have SDL2::Core as a link dependency. + + + +This module will set the following variables in your project: + +:: + + SDL2_MIXER_LIBRARIES, the name of the library to link against + SDL2_MIXER_INCLUDE_DIRS, where to find the headers + SDL2_MIXER_FOUND, if false, do not try to link against + SDL2_MIXER_VERSION_STRING - human-readable string containing the + version of SDL2_mixer + +This module responds to the following cache variables: + +:: + + SDL2_MIXER_PATH + Set a custom SDL2_mixer Library path (default: empty) + + SDL2_MIXER_NO_DEFAULT_PATH + Disable search SDL2_mixer Library in default path. + If SDL2_MIXER_PATH (default: ON) + Else (default: OFF) + + SDL2_MIXER_INCLUDE_DIR + SDL2_mixer headers path. + + SDL2_MIXER_LIBRARY + SDL2_mixer Library (.dll, .so, .a, etc) path. + + +Additional Note: If you see an empty SDL2_MIXER_LIBRARY in your project +configuration, it means CMake did not find your SDL2_mixer library +(SDL2_mixer.dll, libsdl2_mixer.so, etc). Set SDL2_MIXER_LIBRARY to point +to your SDL2_mixer library, and configure again. This value is used to +generate the final SDL2_MIXER_LIBRARIES variable and the SDL2::Mixer target, +but when this value is unset, SDL2_MIXER_LIBRARIES and SDL2::Mixer does not +get created. + + +$SDL2MIXERDIR is an environment variable that would correspond to the +./configure --prefix=$SDL2MIXERDIR used in building SDL2_mixer. + +$SDL2DIR is an environment variable that would correspond to the +./configure --prefix=$SDL2DIR used in building SDL2. + + + +Created by Amine Ben Hassouna: + Adapt FindSDL_mixer.cmake to SDL2_mixer (FindSDL2_mixer.cmake). + Add cache variables for more flexibility: + SDL2_MIXER_PATH, SDL2_MIXER_NO_DEFAULT_PATH (for details, see doc above). + Add SDL2 as a required dependency. + Modernize the FindSDL2_mixer.cmake module by creating a specific target: + SDL2::Mixer (for details, see doc above). + +Original FindSDL_mixer.cmake module: + Created by Eric Wing. This was influenced by the FindSDL.cmake + module, but with modifications to recognize OS X frameworks and + additional Unix paths (FreeBSD, etc). +#]=======================================================================] + +# SDL2 Library required +find_package(SDL2 QUIET) +if(NOT SDL2_FOUND) + set(SDL2_MIXER_SDL2_NOT_FOUND "Could NOT find SDL2 (SDL2 is required by SDL2_mixer).") + if(SDL2_mixer_FIND_REQUIRED) + message(FATAL_ERROR ${SDL2_MIXER_SDL2_NOT_FOUND}) + else() + if(NOT SDL2_mixer_FIND_QUIETLY) + message(STATUS ${SDL2_MIXER_SDL2_NOT_FOUND}) + endif() + return() + endif() + unset(SDL2_MIXER_SDL2_NOT_FOUND) +endif() + + +# Define options for searching SDL2_mixer Library in a custom path + +set(SDL2_MIXER_PATH "" CACHE STRING "Custom SDL2_mixer Library path") + +set(_SDL2_MIXER_NO_DEFAULT_PATH OFF) +if(SDL2_MIXER_PATH) + set(_SDL2_MIXER_NO_DEFAULT_PATH ON) +endif() + +set(SDL2_MIXER_NO_DEFAULT_PATH ${_SDL2_MIXER_NO_DEFAULT_PATH} + CACHE BOOL "Disable search SDL2_mixer Library in default path") +unset(_SDL2_MIXER_NO_DEFAULT_PATH) + +set(SDL2_MIXER_NO_DEFAULT_PATH_CMD) +if(SDL2_MIXER_NO_DEFAULT_PATH) + set(SDL2_MIXER_NO_DEFAULT_PATH_CMD NO_DEFAULT_PATH) +endif() + +# Search for the SDL2_mixer include directory +find_path(SDL2_MIXER_INCLUDE_DIR SDL_mixer.h + HINTS + ENV SDL2MIXERDIR + ENV SDL2DIR + ${SDL2_MIXER_NO_DEFAULT_PATH_CMD} + PATH_SUFFIXES SDL2 + # path suffixes to search inside ENV{SDL2DIR} + # and ENV{SDL2MIXERDIR} + include/SDL2 include + PATHS ${SDL2_MIXER_PATH} + DOC "Where the SDL2_mixer headers can be found" +) + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(VC_LIB_PATH_SUFFIX lib/x64) +else() + set(VC_LIB_PATH_SUFFIX lib/x86) +endif() + +# Search for the SDL2_mixer library +find_library(SDL2_MIXER_LIBRARY + NAMES SDL2_mixer + HINTS + ENV SDL2MIXERDIR + ENV SDL2DIR + ${SDL2_MIXER_NO_DEFAULT_PATH_CMD} + PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX} + PATHS ${SDL2_MIXER_PATH} + DOC "Where the SDL2_mixer Library can be found" +) + +# Read SDL2_mixer version +if(SDL2_MIXER_INCLUDE_DIR AND EXISTS "${SDL2_MIXER_INCLUDE_DIR}/SDL_mixer.h") + file(STRINGS "${SDL2_MIXER_INCLUDE_DIR}/SDL_mixer.h" SDL2_MIXER_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_MIXER_MAJOR_VERSION[ \t]+[0-9]+$") + file(STRINGS "${SDL2_MIXER_INCLUDE_DIR}/SDL_mixer.h" SDL2_MIXER_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_MIXER_MINOR_VERSION[ \t]+[0-9]+$") + file(STRINGS "${SDL2_MIXER_INCLUDE_DIR}/SDL_mixer.h" SDL2_MIXER_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_MIXER_PATCHLEVEL[ \t]+[0-9]+$") + string(REGEX REPLACE "^#define[ \t]+SDL_MIXER_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_MIXER_VERSION_MAJOR "${SDL2_MIXER_VERSION_MAJOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL_MIXER_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_MIXER_VERSION_MINOR "${SDL2_MIXER_VERSION_MINOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL_MIXER_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_MIXER_VERSION_PATCH "${SDL2_MIXER_VERSION_PATCH_LINE}") + set(SDL2_MIXER_VERSION_STRING ${SDL2_MIXER_VERSION_MAJOR}.${SDL2_MIXER_VERSION_MINOR}.${SDL2_MIXER_VERSION_PATCH}) + unset(SDL2_MIXER_VERSION_MAJOR_LINE) + unset(SDL2_MIXER_VERSION_MINOR_LINE) + unset(SDL2_MIXER_VERSION_PATCH_LINE) + unset(SDL2_MIXER_VERSION_MAJOR) + unset(SDL2_MIXER_VERSION_MINOR) + unset(SDL2_MIXER_VERSION_PATCH) +endif() + +set(SDL2_MIXER_LIBRARIES ${SDL2_MIXER_LIBRARY}) +set(SDL2_MIXER_INCLUDE_DIRS ${SDL2_MIXER_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2_mixer + REQUIRED_VARS SDL2_MIXER_LIBRARIES SDL2_MIXER_INCLUDE_DIRS + VERSION_VAR SDL2_MIXER_VERSION_STRING) + + +mark_as_advanced(SDL2_MIXER_PATH + SDL2_MIXER_NO_DEFAULT_PATH + SDL2_MIXER_LIBRARY + SDL2_MIXER_INCLUDE_DIR) + + +if(SDL2_MIXER_FOUND) + + # SDL2::Mixer target + if(SDL2_MIXER_LIBRARY AND NOT TARGET SDL2::Mixer) + add_library(SDL2::Mixer UNKNOWN IMPORTED) + set_target_properties(SDL2::Mixer PROPERTIES + IMPORTED_LOCATION "${SDL2_MIXER_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${SDL2_MIXER_INCLUDE_DIR}" + INTERFACE_LINK_LIBRARIES SDL2::Core) + endif() +endif() diff --git a/cmake/FindSDL2_net.cmake b/cmake/FindSDL2_net.cmake new file mode 100644 index 0000000..74c765b --- /dev/null +++ b/cmake/FindSDL2_net.cmake @@ -0,0 +1,222 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# Copyright 2019 Amine Ben Hassouna +# Copyright 2000-2019 Kitware, Inc. and Contributors +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. + +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. + +# * Neither the name of Kitware, Inc. nor the names of Contributors +# may be used to endorse or promote products derived from this +# software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[=======================================================================[.rst: +FindSDL2_net +------------ + +Locate SDL2_net library + +This module defines the following 'IMPORTED' target: + +:: + + SDL2::Net + The SDL2_net library, if found. + Have SDL2::Core as a link dependency. + + + +This module will set the following variables in your project: + +:: + + SDL2_NET_LIBRARIES, the name of the library to link against + SDL2_NET_INCLUDE_DIRS, where to find the headers + SDL2_NET_FOUND, if false, do not try to link against + SDL2_NET_VERSION_STRING - human-readable string containing the + version of SDL2_net + + + +This module responds to the following cache variables: + +:: + + SDL2_NET_PATH + Set a custom SDL2_net Library path (default: empty) + + SDL2_NET_NO_DEFAULT_PATH + Disable search SDL2_net Library in default path. + If SDL2_NET_PATH (default: ON) + Else (default: OFF) + + SDL2_NET_INCLUDE_DIR + SDL2_net headers path. + + SDL2_NET_LIBRARY + SDL2_net Library (.dll, .so, .a, etc) path. + + +Additional Note: If you see an empty SDL2_NET_LIBRARY in your project +configuration, it means CMake did not find your SDL2_net library +(SDL2_net.dll, libsdl2_net.so, etc). Set SDL2_NET_LIBRARY to point +to your SDL2_net library, and configure again. This value is used to +generate the final SDL2_NET_LIBRARIES variable and the SDL2::Net target, +but when this value is unset, SDL2_NET_LIBRARIES and SDL2::Net does not +get created. + + +$SDL2NETDIR is an environment variable that would correspond to the +./configure --prefix=$SDL2NETDIR used in building SDL2_net. + +$SDL2DIR is an environment variable that would correspond to the +./configure --prefix=$SDL2DIR used in building SDL2. + + + +Created by Amine Ben Hassouna: + Adapt FindSDL_net.cmake to SDL2_net (FindSDL2_net.cmake). + Add cache variables for more flexibility: + SDL2_NET_PATH, SDL2_NET_NO_DEFAULT_PATH (for details, see doc above). + Add SDL2 as a required dependency. + Modernize the FindSDL2_net.cmake module by creating a specific target: + SDL2::Net (for details, see doc above). + +Original FindSDL_net.cmake module: + Created by Eric Wing. This was influenced by the FindSDL.cmake + module, but with modifications to recognize OS X frameworks and + additional Unix paths (FreeBSD, etc). +#]=======================================================================] + +# SDL2 Library required +find_package(SDL2 QUIET) +if(NOT SDL2_FOUND) + set(SDL2_NET_SDL2_NOT_FOUND "Could NOT find SDL2 (SDL2 is required by SDL2_net).") + if(SDL2_net_FIND_REQUIRED) + message(FATAL_ERROR ${SDL2_NET_SDL2_NOT_FOUND}) + else() + if(NOT SDL2_net_FIND_QUIETLY) + message(STATUS ${SDL2_NET_SDL2_NOT_FOUND}) + endif() + return() + endif() + unset(SDL2_NET_SDL2_NOT_FOUND) +endif() + + +# Define options for searching SDL2_net Library in a custom path + +set(SDL2_NET_PATH "" CACHE STRING "Custom SDL2_net Library path") + +set(_SDL2_NET_NO_DEFAULT_PATH OFF) +if(SDL2_NET_PATH) + set(_SDL2_NET_NO_DEFAULT_PATH ON) +endif() + +set(SDL2_NET_NO_DEFAULT_PATH ${_SDL2_NET_NO_DEFAULT_PATH} + CACHE BOOL "Disable search SDL2_net Library in default path") +unset(_SDL2_NET_NO_DEFAULT_PATH) + +set(SDL2_NET_NO_DEFAULT_PATH_CMD) +if(SDL2_NET_NO_DEFAULT_PATH) + set(SDL2_NET_NO_DEFAULT_PATH_CMD NO_DEFAULT_PATH) +endif() + +# Search for the SDL2_net include directory +find_path(SDL2_NET_INCLUDE_DIR SDL_net.h + HINTS + ENV SDL2NETDIR + ENV SDL2DIR + ${SDL2_NET_NO_DEFAULT_PATH_CMD} + PATH_SUFFIXES SDL2 + # path suffixes to search inside ENV{SDL2DIR} + # and ENV{SDL2NETDIR} + include/SDL2 include + PATHS ${SDL2_NET_PATH} + DOC "Where the SDL2_net headers can be found" +) + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(VC_LIB_PATH_SUFFIX lib/x64) +else() + set(VC_LIB_PATH_SUFFIX lib/x86) +endif() + +# Search for the SDL2_net library +find_library(SDL2_NET_LIBRARY + NAMES SDL2_net + HINTS + ENV SDL2NETDIR + ENV SDL2DIR + ${SDL2_NET_NO_DEFAULT_PATH_CMD} + PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX} + PATHS ${SDL2_NET_PATH} + DOC "Where the SDL2_net Library can be found" +) + +# Read SDL2_net version +if(SDL2_NET_INCLUDE_DIR AND EXISTS "${SDL2_NET_INCLUDE_DIR}/SDL_net.h") + file(STRINGS "${SDL2_NET_INCLUDE_DIR}/SDL_net.h" SDL2_NET_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_NET_MAJOR_VERSION[ \t]+[0-9]+$") + file(STRINGS "${SDL2_NET_INCLUDE_DIR}/SDL_net.h" SDL2_NET_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_NET_MINOR_VERSION[ \t]+[0-9]+$") + file(STRINGS "${SDL2_NET_INCLUDE_DIR}/SDL_net.h" SDL2_NET_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_NET_PATCHLEVEL[ \t]+[0-9]+$") + string(REGEX REPLACE "^#define[ \t]+SDL_NET_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_NET_VERSION_MAJOR "${SDL2_NET_VERSION_MAJOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL_NET_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_NET_VERSION_MINOR "${SDL2_NET_VERSION_MINOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL_NET_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_NET_VERSION_PATCH "${SDL2_NET_VERSION_PATCH_LINE}") + set(SDL2_NET_VERSION_STRING ${SDL2_NET_VERSION_MAJOR}.${SDL2_NET_VERSION_MINOR}.${SDL2_NET_VERSION_PATCH}) + unset(SDL2_NET_VERSION_MAJOR_LINE) + unset(SDL2_NET_VERSION_MINOR_LINE) + unset(SDL2_NET_VERSION_PATCH_LINE) + unset(SDL2_NET_VERSION_MAJOR) + unset(SDL2_NET_VERSION_MINOR) + unset(SDL2_NET_VERSION_PATCH) +endif() + +set(SDL2_NET_LIBRARIES ${SDL2_NET_LIBRARY}) +set(SDL2_NET_INCLUDE_DIRS ${SDL2_NET_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2_net + REQUIRED_VARS SDL2_NET_LIBRARIES SDL2_NET_INCLUDE_DIRS + VERSION_VAR SDL2_NET_VERSION_STRING) + + +mark_as_advanced(SDL2_NET_PATH + SDL2_NET_NO_DEFAULT_PATH + SDL2_NET_LIBRARY + SDL2_NET_INCLUDE_DIR) + + +if(SDL2_NET_FOUND) + + # SDL2::Net target + if(SDL2_NET_LIBRARY AND NOT TARGET SDL2::Net) + add_library(SDL2::Net UNKNOWN IMPORTED) + set_target_properties(SDL2::Net PROPERTIES + IMPORTED_LOCATION "${SDL2_NET_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${SDL2_NET_INCLUDE_DIR}" + INTERFACE_LINK_LIBRARIES SDL2::Core) + endif() +endif() diff --git a/cmake/FindVorbis.cmake b/cmake/FindVorbis.cmake new file mode 100644 index 0000000..e50ba19 --- /dev/null +++ b/cmake/FindVorbis.cmake @@ -0,0 +1,76 @@ +# - Find vorbis library +# Find the native Vorbis headers and libraries.Vorbis depends on Ogg and will +# provide Ogg headers/libraries as well. +# +# VORBIS_INCLUDE_DIRS - where to find vorbis/vorbis.h, ogg/ogg.h, etc +# VORBIS_LIBRARIES - List of libraries when using libvorbis +# VORBIS_FOUND - True if vorbis is found. + + +#============================================================================= +#Copyright 2000-2009 Kitware, Inc., Insight Software Consortium +#All rights reserved. +# +#Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +#* Redistributions of source code must retain the above copyright notice, +#this list of conditions and the following disclaimer. +# +#* Redistributions in binary form must reproduce the above copyright notice, +#this list of conditions and the following disclaimer in the documentation +#and/or other materials provided with the distribution. +# +#* Neither the names of Kitware, Inc., the Insight Software Consortium, nor +#the names of their contributors may be used to endorse or promote products +#derived from this software without specific prior written permission. +# +#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +#AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +#IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +#ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +#LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +#CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +#SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +#INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +#CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +#ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +#POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +# Find Ogg +if( Vorbis_FIND_REQUIRED ) + set( OGG_ARG "REQUIRED" ) +elseif( Vorbis_FIND_QUIETLY ) + set( OGG_ARG "QUIETLY" ) +endif() + +find_package( Ogg ${OGG_ARG} ) + +# Look for the vorbisfile header file. +find_path( VORBIS_INCLUDE_DIR + NAMES vorbis/vorbisfile.h + DOC "Vorbis include directory" ) +mark_as_advanced( VORBIS_INCLUDE_DIR ) + +# Look for the vorbisfile library. +find_library( VORBISFILE_LIBRARY + NAMES vorbisfile + DOC "Path to VorbisFile library" ) +mark_as_advanced( VORBISFILE_LIBRARY ) + +# Look for the vorbis library. +find_library( VORBIS_LIBRARY + NAMES vorbis + DOC "Path to Vorbis library" ) +mark_as_advanced( VORBIS_LIBRARY ) + + + +# handle the QUIETLY and REQUIRED arguments and set VORBISFILE_FOUND to TRUE if +# all listed variables are TRUE +include( ${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake ) +FIND_PACKAGE_HANDLE_STANDARD_ARGS( Vorbis DEFAULT_MSG VORBIS_LIBRARY VORBIS_INCLUDE_DIR ) + +set( VORBIS_LIBRARIES ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY} ${OGG_LIBRARIES} ) +set( VORBIS_INCLUDE_DIRS ${VORBIS_INCLUDE_DIR} ${OGG_INCLUDE_DIRS} ) diff --git a/cmake/TargetArch.cmake b/cmake/TargetArch.cmake new file mode 100644 index 0000000..198aa9c --- /dev/null +++ b/cmake/TargetArch.cmake @@ -0,0 +1,157 @@ + +# Copyright (c) 2012 Petroules Corporation. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +# OF SUCH DAMAGE. + +# Based on the Qt 5 processor detection code, so should be very accurate +# https://qt.gitorious.org/qt/qtbase/blobs/master/src/corelib/global/qprocessordetection.h +# Currently handles arm (v5, v6, v7), x86 (32/64), ia64, and ppc (32/64) + +# Regarding POWER/PowerPC, just as is noted in the Qt source, +# "There are many more known variants/revisions that we do not handle/detect." + +set(archdetect_c_code " +#if defined(__arm__) || defined(__TARGET_ARCH_ARM) + #if defined(__ARM_ARCH_7__) \\ + || defined(__ARM_ARCH_7A__) \\ + || defined(__ARM_ARCH_7R__) \\ + || defined(__ARM_ARCH_7M__) \\ + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 7) + #error cmake_ARCH armv7 + #elif defined(__ARM_ARCH_6__) \\ + || defined(__ARM_ARCH_6J__) \\ + || defined(__ARM_ARCH_6T2__) \\ + || defined(__ARM_ARCH_6Z__) \\ + || defined(__ARM_ARCH_6K__) \\ + || defined(__ARM_ARCH_6ZK__) \\ + || defined(__ARM_ARCH_6M__) \\ + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 6) + #error cmake_ARCH armv6 + #elif defined(__ARM_ARCH_5TEJ__) \\ + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 5) + #error cmake_ARCH armv5 + #else + #error cmake_ARCH arm + #endif +#elif defined(__i386) || defined(__i386__) || defined(_M_IX86) + #error cmake_ARCH i386 +#elif defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64) + #error cmake_ARCH x86_64 +#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64) + #error cmake_ARCH ia64 +#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \\ + || defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \\ + || defined(_M_MPPC) || defined(_M_PPC) + #if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__) + #error cmake_ARCH ppc64 + #else + #error cmake_ARCH ppc + #endif +#endif + +#error cmake_ARCH unknown +") + +# Set ppc_support to TRUE before including this file or ppc and ppc64 +# will be treated as invalid architectures since they are no longer supported by Apple + +function(target_architecture output_var) + if(APPLE AND CMAKE_OSX_ARCHITECTURES) + # On OS X we use CMAKE_OSX_ARCHITECTURES *if* it was set + # First let's normalize the order of the values + + # Note that it's not possible to compile PowerPC applications if you are using + # the OS X SDK version 10.6 or later - you'll need 10.4/10.5 for that, so we + # disable it by default + # See this page for more information: + # http://stackoverflow.com/questions/5333490/how-can-we-restore-ppc-ppc64-as-well-as-full-10-4-10-5-sdk-support-to-xcode-4 + + # Architecture defaults to i386 or ppc on OS X 10.5 and earlier, depending on the CPU type detected at runtime. + # On OS X 10.6+ the default is x86_64 if the CPU supports it, i386 otherwise. + + foreach(osx_arch ${CMAKE_OSX_ARCHITECTURES}) + if("${osx_arch}" STREQUAL "ppc" AND ppc_support) + set(osx_arch_ppc TRUE) + elseif("${osx_arch}" STREQUAL "i386") + set(osx_arch_i386 TRUE) + elseif("${osx_arch}" STREQUAL "x86_64") + set(osx_arch_x86_64 TRUE) + elseif("${osx_arch}" STREQUAL "ppc64" AND ppc_support) + set(osx_arch_ppc64 TRUE) + else() + message(FATAL_ERROR "Invalid OS X arch name: ${osx_arch}") + endif() + endforeach() + + # Now add all the architectures in our normalized order + if(osx_arch_ppc) + list(APPEND ARCH ppc) + endif() + + if(osx_arch_i386) + list(APPEND ARCH i386) + endif() + + if(osx_arch_x86_64) + list(APPEND ARCH x86_64) + endif() + + if(osx_arch_ppc64) + list(APPEND ARCH ppc64) + endif() + else() + file(WRITE "${CMAKE_BINARY_DIR}/arch.c" "${archdetect_c_code}") + + enable_language(C) + + # Detect the architecture in a rather creative way... + # This compiles a small C program which is a series of ifdefs that selects a + # particular #error preprocessor directive whose message string contains the + # target architecture. The program will always fail to compile (both because + # file is not a valid C program, and obviously because of the presence of the + # #error preprocessor directives... but by exploiting the preprocessor in this + # way, we can detect the correct target architecture even when cross-compiling, + # since the program itself never needs to be run (only the compiler/preprocessor) + try_run( + run_result_unused + compile_result_unused + "${CMAKE_BINARY_DIR}" + "${CMAKE_BINARY_DIR}/arch.c" + COMPILE_OUTPUT_VARIABLE ARCH + CMAKE_FLAGS CMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} + ) + + # Parse the architecture name from the compiler output + string(REGEX MATCH "cmake_ARCH ([a-zA-Z0-9_]+)" ARCH "${ARCH}") + + # Get rid of the value marker leaving just the architecture name + string(REPLACE "cmake_ARCH " "" ARCH "${ARCH}") + + # If we are compiling with an unknown architecture this variable should + # already be set to "unknown" but in the case that it's empty (i.e. due + # to a typo in the code), then set it to unknown + if (NOT ARCH) + set(ARCH unknown) + endif() + endif() + + set(${output_var} "${ARCH}" PARENT_SCOPE) +endfunction() diff --git a/cmake/config.h.cin b/cmake/config.h.cin new file mode 100644 index 0000000..e58aaec --- /dev/null +++ b/cmake/config.h.cin @@ -0,0 +1,47 @@ +#cmakedefine PACKAGE_NAME "@PACKAGE_NAME@" +#cmakedefine PACKAGE_TARNAME "@PACKAGE_TARNAME@" +#cmakedefine PACKAGE_VERSION "@PACKAGE_VERSION@" +#cmakedefine PACKAGE_HOMEPAGE "@PACKAGE_HOMEPAGE@" +#cmakedefine PACKAGE_STRING "@PACKAGE_STRING@" + +#cmakedefine DOOMWADDIR "@DOOMWADDIR@" +#cmakedefine PRBOOMDATADIR "@PRBOOMDATADIR@" + +#cmakedefine WORDS_BIGENDIAN + +#cmakedefine HAVE_GETOPT +#cmakedefine HAVE_MMAP +#cmakedefine HAVE_CREATE_FILE_MAPPING +#cmakedefine HAVE_SCHED_SETAFFINITY +#cmakedefine HAVE_USLEEP +#cmakedefine HAVE_STRSIGNAL +#cmakedefine HAVE_MKSTEMP + +#cmakedefine HAVE_SYS_WAIT_H +#cmakedefine HAVE_UNISTD_H +#cmakedefine HAVE_ASM_BYTEORDER_H +#cmakedefine HAVE_DIRENT_H + +#cmakedefine HAVE_SDL_JOYSTICKGETAXIS +#cmakedefine HAVE_LIBSDL2_IMAGE +#cmakedefine HAVE_LIBSDL2_MIXER +#cmakedefine HAVE_NET +#cmakedefine USE_SDL_NET + +#cmakedefine HAVE_LIBPCREPOSIX +#cmakedefine HAVE_LIBZ +#cmakedefine HAVE_LIBMAD +#cmakedefine HAVE_LIBFLUIDSYNTH +#cmakedefine HAVE_LIBDUMB +#cmakedefine HAVE_LIBVORBISFILE +#cmakedefine HAVE_LIBPORTMIDI +#cmakedefine HAVE_ALSA + +#cmakedefine SIMPLECHECKS +#cmakedefine ZONEIDCHECK + +#cmakedefine RANGECHECK +#cmakedefine INSTRUMENTED +#cmakedefine TIMEDIAG +#cmakedefine HEAPCHECK +#cmakedefine HEAPDUMP diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt new file mode 100644 index 0000000..9d7c669 --- /dev/null +++ b/data/CMakeLists.txt @@ -0,0 +1,370 @@ +# PrBoom-Plus WAD creation tool + +if(NOT CMAKE_CROSSCOMPILING) + add_executable(rdatawad + rd_main.c + rd_util.c + rd_util.h + rd_output.c + rd_output.h + rd_sound.c + rd_sound.h + rd_palette.c + rd_palette.h + rd_graphic.c + rd_graphic.h + ) + target_include_directories(rdatawad PRIVATE ${CMAKE_BINARY_DIR}) + + set(CROSS_EXPORTS ${CROSS_EXPORTS} rdatawad PARENT_SCOPE) +endif() + +# PrBoom-Plus internal WAD + +set(PALETTE + palette.rgb +) + +set(LUMPS + lumps/switches.lmp + lumps/animated.lmp + lumps/crbrick.lmp + lumps/crtan.lmp + lumps/crgray.lmp + lumps/crgreen.lmp + lumps/crbrown.lmp + lumps/crgold.lmp + lumps/crred.lmp + lumps/crblue.lmp + lumps/crblue2.lmp + lumps/crorange.lmp + lumps/cryellow.lmp + lumps/crblack.lmp + lumps/crpurple.lmp + lumps/crwhite.lmp +) + +set(COLORMAPS + lumps/watermap.lmp +) + +set(TABLES + lumps/sinetabl.lmp + lumps/tangtabl.lmp + lumps/tantoang.lmp + lumps/gammatbl.lmp + lumps/chexdeh.lmp + lumps/bfgbex.lmp + lumps/nervebex.lmp + lumps/glshadow.lmp + lumps/glfp.lmp + lumps/glvp.lmp + lumps/-prbhud-.lmp + lumps/m_ammo.lmp + lumps/m_armour.lmp + lumps/m_arrow.lmp + lumps/m_health.lmp + lumps/m_key.lmp + lumps/m_normal.lmp + lumps/m_shadow.lmp + lumps/m_power.lmp + lumps/m_weap.lmp + lumps/m_mark.lmp +) + +set(SOUNDS + sounds/dsdgsit.wav + sounds/dsdgatk.wav + sounds/dsdgact.wav + sounds/dsdgdth.wav + sounds/dsdgpain.wav +) + +set(GRAPHICS + graphics/dig0.ppm + graphics/dig1.ppm + graphics/dig2.ppm + graphics/dig3.ppm + graphics/dig4.ppm + graphics/dig5.ppm + graphics/dig6.ppm + graphics/dig7.ppm + graphics/dig8.ppm + graphics/dig9.ppm + graphics/diga.ppm + graphics/digb.ppm + graphics/digc.ppm + graphics/digd.ppm + graphics/dige.ppm + graphics/digf.ppm + graphics/digg.ppm + graphics/digh.ppm + graphics/digi.ppm + graphics/digj.ppm + graphics/digk.ppm + graphics/digl.ppm + graphics/digm.ppm + graphics/dign.ppm + graphics/digo.ppm + graphics/digp.ppm + graphics/digq.ppm + graphics/digr.ppm + graphics/digs.ppm + graphics/digt.ppm + graphics/digu.ppm + graphics/digv.ppm + graphics/digw.ppm + graphics/digx.ppm + graphics/digy.ppm + graphics/digz.ppm + graphics/dig45.ppm + graphics/dig46.ppm + graphics/dig47.ppm + graphics/dig58.ppm + graphics/dig91.ppm + graphics/dig93.ppm + graphics/stbr123.ppm + graphics/stbr124.ppm + graphics/stbr125.ppm + graphics/stbr126.ppm + graphics/stbr127.ppm + graphics/boxul.ppm + graphics/boxuc.ppm + graphics/boxur.ppm + graphics/boxcl.ppm + graphics/boxcc.ppm + graphics/boxcr.ppm + graphics/boxll.ppm + graphics/boxlc.ppm + graphics/boxlr.ppm + graphics/stkeys6.ppm + graphics/stkeys7.ppm + graphics/stkeys8.ppm + graphics/stcfn096.ppm + graphics/m_butt1.ppm + graphics/m_butt2.ppm + graphics/m_colors.ppm + graphics/m_palno.ppm + graphics/m_palsel.ppm + graphics/m_vbox.ppm + graphics/cross1.ppm + graphics/cross2.ppm + graphics/cross3.ppm +) + +set(FLATS + flats/-n0_tex-.ppm +) + +set(SPRITES + sprites/tnt1a0.ppm +) +set(SPRITEP "0,0,sprites/tnt1a0.ppm") + +set(SPRITES + ${SPRITES} + sprites/dogsa1.ppm + sprites/dogsa2.ppm + sprites/dogsa3.ppm + sprites/dogsa4.ppm + sprites/dogsa5.ppm + sprites/dogsa6.ppm + sprites/dogsa7.ppm + sprites/dogsa8.ppm + sprites/dogsb1.ppm + sprites/dogsb2.ppm + sprites/dogsb3.ppm + sprites/dogsb4.ppm + sprites/dogsb5.ppm + sprites/dogsb6.ppm + sprites/dogsb7.ppm + sprites/dogsb8.ppm + sprites/dogsc1.ppm + sprites/dogsc2.ppm + sprites/dogsc3.ppm + sprites/dogsc4.ppm + sprites/dogsc5.ppm + sprites/dogsc6.ppm + sprites/dogsc7.ppm + sprites/dogsc8.ppm + sprites/dogsd1.ppm + sprites/dogsd2.ppm + sprites/dogsd3.ppm + sprites/dogsd4.ppm + sprites/dogsd5.ppm + sprites/dogsd6.ppm + sprites/dogsd7.ppm + sprites/dogsd8.ppm + sprites/dogse1.ppm + sprites/dogse2.ppm + sprites/dogse3.ppm + sprites/dogse4.ppm + sprites/dogse5.ppm + sprites/dogse6.ppm + sprites/dogse7.ppm + sprites/dogse8.ppm + sprites/dogsf1.ppm + sprites/dogsf2.ppm + sprites/dogsf3.ppm + sprites/dogsf4.ppm + sprites/dogsf5.ppm + sprites/dogsf6.ppm + sprites/dogsf7.ppm + sprites/dogsf8.ppm + sprites/dogsg1.ppm + sprites/dogsg2.ppm + sprites/dogsg3.ppm + sprites/dogsg4.ppm + sprites/dogsg5.ppm + sprites/dogsg6.ppm + sprites/dogsg7.ppm + sprites/dogsg8.ppm + sprites/dogsh1.ppm + sprites/dogsh2.ppm + sprites/dogsh3.ppm + sprites/dogsh4.ppm + sprites/dogsh5.ppm + sprites/dogsh6.ppm + sprites/dogsh7.ppm + sprites/dogsh8.ppm + sprites/dogsi0.ppm + sprites/dogsj0.ppm + sprites/dogsk0.ppm + sprites/dogsl0.ppm + sprites/dogsm0.ppm + sprites/dogsn0.ppm +) +set(SPRITEP + ${SPRITEP} + "33,66,sprites/dogsa1.ppm" + "33,66,sprites/dogsa2.ppm" + "33,66,sprites/dogsa3.ppm" + "33,66,sprites/dogsa4.ppm" + "33,66,sprites/dogsa5.ppm" + "33,66,sprites/dogsa6.ppm" + "33,66,sprites/dogsa7.ppm" + "33,66,sprites/dogsa8.ppm" + "33,66,sprites/dogsb1.ppm" + "33,66,sprites/dogsb2.ppm" + "33,66,sprites/dogsb3.ppm" + "33,66,sprites/dogsb4.ppm" + "33,66,sprites/dogsb5.ppm" + "33,66,sprites/dogsb6.ppm" + "33,66,sprites/dogsb7.ppm" + "33,66,sprites/dogsb8.ppm" + "33,66,sprites/dogsc1.ppm" + "33,66,sprites/dogsc2.ppm" + "33,66,sprites/dogsc3.ppm" + "33,66,sprites/dogsc4.ppm" + "33,66,sprites/dogsc5.ppm" + "33,66,sprites/dogsc6.ppm" + "33,66,sprites/dogsc7.ppm" + "33,66,sprites/dogsc8.ppm" + "33,66,sprites/dogsd1.ppm" + "33,66,sprites/dogsd2.ppm" + "33,66,sprites/dogsd3.ppm" + "33,66,sprites/dogsd4.ppm" + "33,66,sprites/dogsd5.ppm" + "33,66,sprites/dogsd6.ppm" + "33,66,sprites/dogsd7.ppm" + "33,66,sprites/dogsd8.ppm" + "33,66,sprites/dogse1.ppm" + "33,66,sprites/dogse2.ppm" + "33,66,sprites/dogse3.ppm" + "33,66,sprites/dogse4.ppm" + "33,66,sprites/dogse5.ppm" + "33,66,sprites/dogse6.ppm" + "33,66,sprites/dogse7.ppm" + "33,66,sprites/dogse8.ppm" + "33,66,sprites/dogsf1.ppm" + "33,66,sprites/dogsf2.ppm" + "33,66,sprites/dogsf3.ppm" + "33,66,sprites/dogsf4.ppm" + "33,66,sprites/dogsf5.ppm" + "33,66,sprites/dogsf6.ppm" + "33,66,sprites/dogsf7.ppm" + "33,66,sprites/dogsf8.ppm" + "33,66,sprites/dogsg1.ppm" + "33,66,sprites/dogsg2.ppm" + "33,66,sprites/dogsg3.ppm" + "33,66,sprites/dogsg4.ppm" + "33,66,sprites/dogsg5.ppm" + "33,66,sprites/dogsg6.ppm" + "33,66,sprites/dogsg7.ppm" + "33,66,sprites/dogsg8.ppm" + "33,66,sprites/dogsh1.ppm" + "33,66,sprites/dogsh2.ppm" + "33,66,sprites/dogsh3.ppm" + "33,66,sprites/dogsh4.ppm" + "33,66,sprites/dogsh5.ppm" + "33,66,sprites/dogsh6.ppm" + "33,66,sprites/dogsh7.ppm" + "33,66,sprites/dogsh8.ppm" + "33,67,sprites/dogsi0.ppm" + "33,67,sprites/dogsj0.ppm" + "33,67,sprites/dogsk0.ppm" + "33,67,sprites/dogsl0.ppm" + "33,68,sprites/dogsm0.ppm" + "33,69,sprites/dogsn0.ppm" +) + +set(SPRITES + ${SPRITES} + sprites/pls1a0.ppm + sprites/pls1b0.ppm + sprites/pls1c0.ppm + sprites/pls1d0.ppm + sprites/pls1e0.ppm + sprites/pls1f0.ppm + sprites/pls1g0.ppm + sprites/pls2a0.ppm + sprites/pls2b0.ppm + sprites/pls2c0.ppm + sprites/pls2d0.ppm + sprites/pls2e0.ppm +) +set(SPRITEP + ${SPRITEP} + "9,11,sprites/pls1a0.ppm" + "8,11,sprites/pls1b0.ppm" + "9,11,sprites/pls1c0.ppm" + "8,11,sprites/pls1d0.ppm" + "16,27,sprites/pls1e0.ppm" + "16,27,sprites/pls1f0.ppm" + "18,27,sprites/pls1g0.ppm" + "9,11,sprites/pls2a0.ppm" + "8,13,sprites/pls2b0.ppm" + "11,18,sprites/pls2c0.ppm" + "16,27,sprites/pls2d0.ppm" + "18,27,sprites/pls2e0.ppm" +) + +set(WAD_SRC + ${PALETTE} + ${LUMPS} + ${COLORMAPS} + ${TABLES} + ${SOUNDS} + ${GRAPHICS} + ${FLATS} + ${SPRITES} +) + +set(WAD_CMDLINE + -palette ${PALETTE} + -lumps ${LUMPS} + -marker C_START -lumps ${COLORMAPS} -marker C_END + -marker B_START -lumps ${TABLES} -marker B_END + -sounds ${SOUNDS} + -graphics ${GRAPHICS} + -marker FF_START -flats ${FLATS} -marker FF_END + -marker SS_START -sprites ${SPRITEP} -marker SS_END +) + +add_custom_command( + OUTPUT "${WAD_DATA_PATH}" + COMMAND rdatawad -I "${CMAKE_CURRENT_SOURCE_DIR}" ${WAD_CMDLINE} -o "${WAD_DATA_PATH}" + DEPENDS rdatawad ${WAD_SRC} +) +add_custom_target(prboomwad DEPENDS ${WAD_DATA_PATH}) +install(FILES ${WAD_DATA_PATH} DESTINATION ${PRBOOMDATADIR} COMPONENT "PrBoom-Plus internal WAD") diff --git a/data/convert-icon b/data/convert-icon new file mode 100644 index 0000000..c651bc7 --- /dev/null +++ b/data/convert-icon @@ -0,0 +1,74 @@ +#!/usr/bin/python +# +# $Id: convert-icon 704 2006-10-18 00:51:11Z fraggle $ +# +# Copyright(C) 2005 Simon Howard +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +# Converts images into C structures to be inserted in programs +# + +import sys +import os +import re + +try: + import Image +except ImportError: + print "WARNING: Could not update %s. Please install the Python Imaging library." % sys.argv[2] + sys.exit(0) + + +def convert_image(filename, output_filename): + + im = Image.open(filename).convert("RGB") + + outfile = open(output_filename, "w") + + size = im.size + + struct_name = os.path.basename(output_filename) + struct_name = re.sub(re.compile("\\..*$"), "", struct_name) + struct_name = re.sub(re.compile("\W"), "_", struct_name) + + outfile.write("static int %s_w = %i;\n" % (struct_name, size[0])) + outfile.write("static int %s_h = %i;\n" % (struct_name, size[1])) + + outfile.write("\n") + outfile.write("static unsigned char %s_data[] = {\n" % (struct_name)) + + elements_on_line = 0 + + outfile.write(" ") + + for y in range(size[1]): + for x in range(size[0]): + val = im.getpixel((x, y)) + outfile.write("0x%02x,0x%02x,0x%02x, " % val) + elements_on_line += 1 + + if elements_on_line >= 4: + elements_on_line = 0 + outfile.write("\n") + outfile.write(" ") + + outfile.write("\n") + outfile.write("};\n") + +convert_image(sys.argv[1], sys.argv[2]) + + diff --git a/data/flats/-n0_tex-.ppm b/data/flats/-n0_tex-.ppm new file mode 100644 index 0000000..cb8f4ab Binary files /dev/null and b/data/flats/-n0_tex-.ppm differ diff --git a/data/graphics/boxcc.ppm b/data/graphics/boxcc.ppm new file mode 100644 index 0000000..44f6316 --- /dev/null +++ b/data/graphics/boxcc.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +C3?/3+?/C3?/3+?/C3?/C3?/?/3+?/3+C33+?/3+?/?/?/?/?/?/3+?/C3?/C3?/?/?/3+?/?/?/?/?/?/?/?/?/3+?/3+C33+?/3+?/?/C3?/C3?/3+?/C3?/3+?/C3 \ No newline at end of file diff --git a/data/graphics/boxcl.ppm b/data/graphics/boxcl.ppm new file mode 100644 index 0000000..f7b9a1a --- /dev/null +++ b/data/graphics/boxcl.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +#+7#C3C3C3?/C3#+# 7#C3K7C33+C3/7# 7#K7C3C3?/?/#+# 7#?/C3?/?/?//7# 7#C3C3?/3+?//77#C3?/C3?/?/#+# 7#C3C33+C33+/7# 7#C3C3C3?/3+ \ No newline at end of file diff --git a/data/graphics/boxcr.ppm b/data/graphics/boxcr.ppm new file mode 100644 index 0000000..e777dd5 --- /dev/null +++ b/data/graphics/boxcr.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +3+?/C3C3C37##+3+C33+C3C37## #+?/?/C3?/C37## /7C3C3K7K7C37## #+C3?/C3K7K77## /7?/?/C3C3K77#/7C33+C3K7C37## #+C3?/C3C3C37## /7 \ No newline at end of file diff --git a/data/graphics/boxlc.ppm b/data/graphics/boxlc.ppm new file mode 100644 index 0000000..cb67f18 --- /dev/null +++ b/data/graphics/boxlc.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +?/?/3+?/C3?/C3?/?/?/3+?/?/?/?/?/?/?/?/?/3+?/3+C33+?/3+?/?/C3?/C3?/3+?/C3?/3+?/C3/77?'/77?'/7/77?'7?'/7#+/7#+#+/7#+/7# # # # # # \ No newline at end of file diff --git a/data/graphics/boxll.ppm b/data/graphics/boxll.ppm new file mode 100644 index 0000000..fc5cdc4 --- /dev/null +++ b/data/graphics/boxll.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +#+# 7#?/C3?/?/?//7# 7#C3C3?/3+?//77#C3?/C3?/?/#+# 7#C3C33+C33+/7# 7#C3C3C3?/3+#+# #+7?'/7/77?'7?'/7#+#+#+#+/7#+/7#+# # # # # \ No newline at end of file diff --git a/data/graphics/boxlr.ppm b/data/graphics/boxlr.ppm new file mode 100644 index 0000000..6e399a9 --- /dev/null +++ b/data/graphics/boxlr.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +C3C3K7K7C37## #+C3?/C3K7K77## /7?/?/C3C3K77#/7C33+C3K7C37## #+C3?/C3C3C37## /7/77?'/77?'/7/7# #+/7#+/7#+/7#+#+/7# # # # # #+ \ No newline at end of file diff --git a/data/graphics/boxuc.ppm b/data/graphics/boxuc.ppm new file mode 100644 index 0000000..d298b2e --- /dev/null +++ b/data/graphics/boxuc.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +/7#+/7#+#+/7#+/7# # # # # # / / / / / / / / C3?/3+?/C3?/3+?/C3?/C3?/?/3+?/3+C33+?/3+?/?/?/?/?/?/3+?/C3?/C3?/?/?/3+?/?/?/?/?/ \ No newline at end of file diff --git a/data/graphics/boxul.ppm b/data/graphics/boxul.ppm new file mode 100644 index 0000000..1301b8d --- /dev/null +++ b/data/graphics/boxul.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +7?'#+#+#+#+/7#+/7#+# # # # # #+# / / / / / / #+7#C3C3C3?/C3#+# 7#C3K7C33+C3/7# 7#K7C3C3?/?/#+# 7#?/C3?/?/?//7# 7#C3C3?/3+?/ \ No newline at end of file diff --git a/data/graphics/boxur.ppm b/data/graphics/boxur.ppm new file mode 100644 index 0000000..15e0b7c --- /dev/null +++ b/data/graphics/boxur.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +/7#+/7#+/7#+#+7?'# # # # # #+/ / / / / / # #+3+?/C3C3C37##+3+C33+C3C37## #+?/?/C3?/C37## /7C3C3K7K7C37## #+C3?/C3K7K77## /7 \ No newline at end of file diff --git a/data/graphics/cross1.ppm b/data/graphics/cross1.ppm new file mode 100644 index 0000000..673506c Binary files /dev/null and b/data/graphics/cross1.ppm differ diff --git a/data/graphics/cross2.ppm b/data/graphics/cross2.ppm new file mode 100644 index 0000000..7435aaa Binary files /dev/null and b/data/graphics/cross2.ppm differ diff --git a/data/graphics/cross3.ppm b/data/graphics/cross3.ppm new file mode 100644 index 0000000..db40ef1 Binary files /dev/null and b/data/graphics/cross3.ppm differ diff --git a/data/graphics/dig0.ppm b/data/graphics/dig0.ppm new file mode 100644 index 0000000..e0c2e58 Binary files /dev/null and b/data/graphics/dig0.ppm differ diff --git a/data/graphics/dig1.ppm b/data/graphics/dig1.ppm new file mode 100644 index 0000000..9885309 Binary files /dev/null and b/data/graphics/dig1.ppm differ diff --git a/data/graphics/dig2.ppm b/data/graphics/dig2.ppm new file mode 100644 index 0000000..044ccc0 Binary files /dev/null and b/data/graphics/dig2.ppm differ diff --git a/data/graphics/dig3.ppm b/data/graphics/dig3.ppm new file mode 100644 index 0000000..ac3f271 Binary files /dev/null and b/data/graphics/dig3.ppm differ diff --git a/data/graphics/dig4.ppm b/data/graphics/dig4.ppm new file mode 100644 index 0000000..e462df0 Binary files /dev/null and b/data/graphics/dig4.ppm differ diff --git a/data/graphics/dig45.ppm b/data/graphics/dig45.ppm new file mode 100644 index 0000000..3d7a258 Binary files /dev/null and b/data/graphics/dig45.ppm differ diff --git a/data/graphics/dig46.ppm b/data/graphics/dig46.ppm new file mode 100644 index 0000000..205441e Binary files /dev/null and b/data/graphics/dig46.ppm differ diff --git a/data/graphics/dig47.ppm b/data/graphics/dig47.ppm new file mode 100644 index 0000000..a373e97 Binary files /dev/null and b/data/graphics/dig47.ppm differ diff --git a/data/graphics/dig5.ppm b/data/graphics/dig5.ppm new file mode 100644 index 0000000..7566fde Binary files /dev/null and b/data/graphics/dig5.ppm differ diff --git a/data/graphics/dig58.ppm b/data/graphics/dig58.ppm new file mode 100644 index 0000000..8bdaea3 Binary files /dev/null and b/data/graphics/dig58.ppm differ diff --git a/data/graphics/dig6.ppm b/data/graphics/dig6.ppm new file mode 100644 index 0000000..6234633 Binary files /dev/null and b/data/graphics/dig6.ppm differ diff --git a/data/graphics/dig7.ppm b/data/graphics/dig7.ppm new file mode 100644 index 0000000..0838e0d Binary files /dev/null and b/data/graphics/dig7.ppm differ diff --git a/data/graphics/dig8.ppm b/data/graphics/dig8.ppm new file mode 100644 index 0000000..f26de7e Binary files /dev/null and b/data/graphics/dig8.ppm differ diff --git a/data/graphics/dig9.ppm b/data/graphics/dig9.ppm new file mode 100644 index 0000000..ff4d30d Binary files /dev/null and b/data/graphics/dig9.ppm differ diff --git a/data/graphics/dig91.ppm b/data/graphics/dig91.ppm new file mode 100644 index 0000000..d22214e Binary files /dev/null and b/data/graphics/dig91.ppm differ diff --git a/data/graphics/dig93.ppm b/data/graphics/dig93.ppm new file mode 100644 index 0000000..714c9eb Binary files /dev/null and b/data/graphics/dig93.ppm differ diff --git a/data/graphics/diga.ppm b/data/graphics/diga.ppm new file mode 100644 index 0000000..a7f5a43 Binary files /dev/null and b/data/graphics/diga.ppm differ diff --git a/data/graphics/digb.ppm b/data/graphics/digb.ppm new file mode 100644 index 0000000..e6ef562 Binary files /dev/null and b/data/graphics/digb.ppm differ diff --git a/data/graphics/digc.ppm b/data/graphics/digc.ppm new file mode 100644 index 0000000..a5ab712 Binary files /dev/null and b/data/graphics/digc.ppm differ diff --git a/data/graphics/digd.ppm b/data/graphics/digd.ppm new file mode 100644 index 0000000..c57e815 Binary files /dev/null and b/data/graphics/digd.ppm differ diff --git a/data/graphics/dige.ppm b/data/graphics/dige.ppm new file mode 100644 index 0000000..47720c4 Binary files /dev/null and b/data/graphics/dige.ppm differ diff --git a/data/graphics/digf.ppm b/data/graphics/digf.ppm new file mode 100644 index 0000000..58db7b6 Binary files /dev/null and b/data/graphics/digf.ppm differ diff --git a/data/graphics/digg.ppm b/data/graphics/digg.ppm new file mode 100644 index 0000000..e8260e9 Binary files /dev/null and b/data/graphics/digg.ppm differ diff --git a/data/graphics/digh.ppm b/data/graphics/digh.ppm new file mode 100644 index 0000000..b656b6c Binary files /dev/null and b/data/graphics/digh.ppm differ diff --git a/data/graphics/digi.ppm b/data/graphics/digi.ppm new file mode 100644 index 0000000..0664d3a Binary files /dev/null and b/data/graphics/digi.ppm differ diff --git a/data/graphics/digj.ppm b/data/graphics/digj.ppm new file mode 100644 index 0000000..893329c Binary files /dev/null and b/data/graphics/digj.ppm differ diff --git a/data/graphics/digk.ppm b/data/graphics/digk.ppm new file mode 100644 index 0000000..c27a9a0 Binary files /dev/null and b/data/graphics/digk.ppm differ diff --git a/data/graphics/digl.ppm b/data/graphics/digl.ppm new file mode 100644 index 0000000..ecc7646 Binary files /dev/null and b/data/graphics/digl.ppm differ diff --git a/data/graphics/digm.ppm b/data/graphics/digm.ppm new file mode 100644 index 0000000..8830661 Binary files /dev/null and b/data/graphics/digm.ppm differ diff --git a/data/graphics/dign.ppm b/data/graphics/dign.ppm new file mode 100644 index 0000000..c150ddb Binary files /dev/null and b/data/graphics/dign.ppm differ diff --git a/data/graphics/digo.ppm b/data/graphics/digo.ppm new file mode 100644 index 0000000..e0c2e58 Binary files /dev/null and b/data/graphics/digo.ppm differ diff --git a/data/graphics/digp.ppm b/data/graphics/digp.ppm new file mode 100644 index 0000000..d2720e1 Binary files /dev/null and b/data/graphics/digp.ppm differ diff --git a/data/graphics/digq.ppm b/data/graphics/digq.ppm new file mode 100644 index 0000000..d6bada2 Binary files /dev/null and b/data/graphics/digq.ppm differ diff --git a/data/graphics/digr.ppm b/data/graphics/digr.ppm new file mode 100644 index 0000000..43e6dfd Binary files /dev/null and b/data/graphics/digr.ppm differ diff --git a/data/graphics/digs.ppm b/data/graphics/digs.ppm new file mode 100644 index 0000000..7566fde Binary files /dev/null and b/data/graphics/digs.ppm differ diff --git a/data/graphics/digt.ppm b/data/graphics/digt.ppm new file mode 100644 index 0000000..02c024a Binary files /dev/null and b/data/graphics/digt.ppm differ diff --git a/data/graphics/digu.ppm b/data/graphics/digu.ppm new file mode 100644 index 0000000..c2eaf15 Binary files /dev/null and b/data/graphics/digu.ppm differ diff --git a/data/graphics/digv.ppm b/data/graphics/digv.ppm new file mode 100644 index 0000000..0951ea5 Binary files /dev/null and b/data/graphics/digv.ppm differ diff --git a/data/graphics/digw.ppm b/data/graphics/digw.ppm new file mode 100644 index 0000000..aebe8e2 Binary files /dev/null and b/data/graphics/digw.ppm differ diff --git a/data/graphics/digx.ppm b/data/graphics/digx.ppm new file mode 100644 index 0000000..e855035 Binary files /dev/null and b/data/graphics/digx.ppm differ diff --git a/data/graphics/digy.ppm b/data/graphics/digy.ppm new file mode 100644 index 0000000..79d8581 Binary files /dev/null and b/data/graphics/digy.ppm differ diff --git a/data/graphics/digz.ppm b/data/graphics/digz.ppm new file mode 100644 index 0000000..6b650e8 Binary files /dev/null and b/data/graphics/digz.ppm differ diff --git a/data/graphics/m_butt1.ppm b/data/graphics/m_butt1.ppm new file mode 100644 index 0000000..28ce5e3 Binary files /dev/null and b/data/graphics/m_butt1.ppm differ diff --git a/data/graphics/m_butt2.ppm b/data/graphics/m_butt2.ppm new file mode 100644 index 0000000..90d2195 Binary files /dev/null and b/data/graphics/m_butt2.ppm differ diff --git a/data/graphics/m_colors.ppm b/data/graphics/m_colors.ppm new file mode 100644 index 0000000..29b415d Binary files /dev/null and b/data/graphics/m_colors.ppm differ diff --git a/data/graphics/m_palno.ppm b/data/graphics/m_palno.ppm new file mode 100644 index 0000000..0112433 Binary files /dev/null and b/data/graphics/m_palno.ppm differ diff --git a/data/graphics/m_palsel.ppm b/data/graphics/m_palsel.ppm new file mode 100644 index 0000000..4d5968a Binary files /dev/null and b/data/graphics/m_palsel.ppm differ diff --git a/data/graphics/m_vbox.ppm b/data/graphics/m_vbox.ppm new file mode 100644 index 0000000..3a4135e Binary files /dev/null and b/data/graphics/m_vbox.ppm differ diff --git a/data/graphics/stbr123.ppm b/data/graphics/stbr123.ppm new file mode 100644 index 0000000..207f219 Binary files /dev/null and b/data/graphics/stbr123.ppm differ diff --git a/data/graphics/stbr124.ppm b/data/graphics/stbr124.ppm new file mode 100644 index 0000000..272d6f7 Binary files /dev/null and b/data/graphics/stbr124.ppm differ diff --git a/data/graphics/stbr125.ppm b/data/graphics/stbr125.ppm new file mode 100644 index 0000000..c860d62 Binary files /dev/null and b/data/graphics/stbr125.ppm differ diff --git a/data/graphics/stbr126.ppm b/data/graphics/stbr126.ppm new file mode 100644 index 0000000..11c8ee3 Binary files /dev/null and b/data/graphics/stbr126.ppm differ diff --git a/data/graphics/stbr127.ppm b/data/graphics/stbr127.ppm new file mode 100644 index 0000000..2390e74 Binary files /dev/null and b/data/graphics/stbr127.ppm differ diff --git a/data/graphics/stcfn096.ppm b/data/graphics/stcfn096.ppm new file mode 100644 index 0000000..73062ed Binary files /dev/null and b/data/graphics/stcfn096.ppm differ diff --git a/data/graphics/stkeys6.ppm b/data/graphics/stkeys6.ppm new file mode 100644 index 0000000..48c2434 Binary files /dev/null and b/data/graphics/stkeys6.ppm differ diff --git a/data/graphics/stkeys7.ppm b/data/graphics/stkeys7.ppm new file mode 100644 index 0000000..ca338b4 Binary files /dev/null and b/data/graphics/stkeys7.ppm differ diff --git a/data/graphics/stkeys8.ppm b/data/graphics/stkeys8.ppm new file mode 100644 index 0000000..fd84218 Binary files /dev/null and b/data/graphics/stkeys8.ppm differ diff --git a/data/lumps/-prbhud-.lmp b/data/lumps/-prbhud-.lmp new file mode 100644 index 0000000..49c3ec7 --- /dev/null +++ b/data/lumps/-prbhud-.lmp @@ -0,0 +1,153 @@ +hud 0 + +hud 1 +tracers 2 151 +hudadd 2 159 +weapon 2 167 +ammo 2 175 +health 2 183 +armor 2 191 + +hud 2 +tracers 2 135 +hudadd 2 143 +monsec 2 151 +keys 2 159 +weapon 2 167 +ammo 2 175 +health 2 183 +armor 2 191 + +hud 3 +tracers 2 184 +hudadd 2 192 +weapon 208 183 +ammo 208 191 +health 224 2 +armor 224 10 + +hud 4 +tracers 2 167 +hudadd 2 175 +monsec 2 183 +keys 2 191 +weapon 208 183 +ammo 208 191 +health 224 2 +armor 224 10 + +hud 5 +tracers 2 167 +medict_icon_big 4 -198 +health_big 38 -198 +armor_icon_big -316 -198 +armor_big -283 -198 +gkeys -316 4 + +hud 6 +tracers 2 167 +medict_icon_big 4 -198 +health_big 38 -198 +ammo_icon -100 -198 +ammo_big 105 -198 +armor_icon_big -316 -198 +armor_big -283 -198 +gkeys -316 4 + +hud 7 +tracers 2 167 +medict_icon_big 4 -198 +health_big 38 -198 +ammo_icon -100 -198 +ammo_big 105 -198 +armor_icon_big -316 -198 +armor_big -283 -198 +weapon -316 2 +gkeys -316 11 + +hud 8 +tracers 2 147 +medict_icon_big 4 -178 +health_big 38 -178 +armor_icon_big 4 -198 +armor_big 38 -198 +ammo_icon -316 -198 +ammo_big -296 -198 +gkeys -316 4 + +hud 9 +tracers 2 151 +hudadd 2 159 +monsec 2 167 +keys 2 175 +weapon 2 183 +ammo 2 191 +medict_icon_big 242 -178 +health_big -316 -178 +armor_icon_big 240 -198 +armor_big -316 -198 + +hud 10 +tracers 2 151 +hudadd 2 159 +monsec 2 167 +keys 2 175 +weapon 2 183 +ammo 2 191 +armor_icon_small 240 -177 +armor_big -302 -178 +armor_percent -318 -178 +medict_icon_small 241 -197 +health_big -302 -198 +medict_percent -318 -198 + +hud 11 +tracers 2 151 +hudadd 2 159 +monsec 2 167 +keys 2 175 +weapon 2 183 +ammo 2 191 +medict_icon_small -315 -179 +health_big -298 -180 +armor_icon_small -316 -197 +armor_big -298 -198 + +hud 12 +tracers 2 148 +hudadd 2 156 +monsec 2 164 +keys 2 172 +weapon 208 164 +ammo 208 172 +medict_icon_small 2 -197 +health_big 22 -198 +armor_icon_small -316 -197 +armor_big -298 -198 + + +;hud 13 +;tracers 2 151 +;hudadd 2 159 +;monsec 2 167 +;keys 2 175 +;weapon 2 183 +;ammo 2 191 +;armor_icon_custom 230 -178 +;armor_big -302 -178 +;armor_percent -318 -178 +;medict_icon_custom 232 -197 +;health_big -302 -198 +;medict_percent -318 -198 + +hud 13 +tracers 2 151 +hudadd 2 159 +keys 24 168 +weapon 24 177 +ammo_icon 4 -197 +ammo 24 186 +health -297 168 +medict_icon_small -315 -179 +armor -297 186 +armor_icon_small -316 -197 diff --git a/data/lumps/animated.lmp b/data/lumps/animated.lmp new file mode 100644 index 0000000..1ceffce Binary files /dev/null and b/data/lumps/animated.lmp differ diff --git a/data/lumps/b_end.lmp b/data/lumps/b_end.lmp new file mode 100644 index 0000000..e69de29 diff --git a/data/lumps/b_start.lmp b/data/lumps/b_start.lmp new file mode 100644 index 0000000..e69de29 diff --git a/data/lumps/bfgbex.lmp b/data/lumps/bfgbex.lmp new file mode 100644 index 0000000..5c0b030 --- /dev/null +++ b/data/lumps/bfgbex.lmp @@ -0,0 +1,3 @@ +[STRINGS] +HUSTR_31 = level 31: idkfa +HUSTR_32 = level 32: keen diff --git a/data/lumps/c_end.lmp b/data/lumps/c_end.lmp new file mode 100644 index 0000000..e69de29 diff --git a/data/lumps/c_start.lmp b/data/lumps/c_start.lmp new file mode 100644 index 0000000..e69de29 diff --git a/data/lumps/chexdeh.lmp b/data/lumps/chexdeh.lmp new file mode 100644 index 0000000..b4621b3 --- /dev/null +++ b/data/lumps/chexdeh.lmp @@ -0,0 +1,811 @@ +Patch File for DeHackEd v3.0 + +# +# This is a dehacked patch for emulating the chex.exe executable that +# comes with Chex Quest. It is generated from automatic scripts used +# to compare chex.exe with Final Doom's doom2.exe. The purpose of +# this patch is to allow Chex Quest to be played accurately with +# source ports; it will not work with Vanilla Doom and DOS dehacked, +# as some of the string and cheat replacements are longer than is +# possible with dehacked. +# +# Because of the limitations of dehacked, it is not possible to +# completely emulate chex.exe merely with a dehacked patch. Although +# this patch takes care of the majority of the changes necessary, the +# following changes are also necessary to accurately emulate chex.deh: +# +# * Monsters should not drop ammo +# * The game should end after the fifth level, instead of the eighth. +# * The episode selection screen should not be displayed. +# * The level warp cheat should always warp to episode 1. +# * The automap should show the level name for the episode 1 level, +# eg. the displayed level name for E2M3 is the level name for +# E1M3. +# +# Simon Howard (fraggle) +#-- +# +# These are magic commands to Chocolate Doom to enable longer strings +# and cheats than Vanilla dehacked allows: +# +# *allow-long-strings* *allow-long-cheats* +# + +Doom version = 21 +Patch format = 6 + +Text 46 47 +are you sure you want to +quit this great game?Don't give up now...do +you still wish to quit? + +Text 49 38 +please don't leave, there's more +demons to toast!please don't leave, we +need your help! + +Text 50 38 +let's beat it -- this is turning +into a bloodbath!please don't leave, we +need your help! + +Text 50 38 +i wouldn't leave if i were you. +dos is much worse.please don't leave, we +need your help! + +Text 56 38 +you're trying to say you like dos +better than me, right?please don't leave, we +need your help! + +Text 54 38 +don't leave yet -- there's a +demon around that corner!please don't leave, we +need your help! + +Text 55 38 +ya know, next time you come in here +i'm gonna toast ya.please don't leave, we +need your help! + +Text 34 38 +go ahead and leave. see if i care.please don't leave, we +need your help! + +Text 46 47 +are you sure you want to +quit this great game?Don't give up now...do +you still wish to quit? + +Text 49 38 +you want to quit? +then, thou hast lost an eighth!please don't leave, we +need your help! + +Text 72 38 +don't go now, there's a +dimensional shambler waiting +at the dos prompt!please don't leave, we +need your help! + +Text 51 38 +get outta here and go back +to your boring programs.please don't leave, we +need your help! + +Text 53 38 +if i were your boss, i'd + deathmatch ya in a minute!please don't leave, we +need your help! + +Text 57 38 +look, bud. you leave now +and you forfeit your body count!please don't leave, we +need your help! + +Text 59 38 +just leave. when you come +back, i'll be waiting with a bat.please don't leave, we +need your help! + +Text 58 38 +you're lucky i don't smack +you for thinking about leaving.please don't leave, we +need your help! + +Text 72 70 +free memory available for DOOM to execute. Reconfigure your CONFIG.SYS +free memory available for Chex(R) Quest. Reconfigure your CONFIG.SYS + + +Text 73 70 +creating a custom boot menu item in your CONFIG.SYS for optimum DOOMing. +creating a custom boot menu item in your CONFIG.SYS for optimum play. + + +Text 53 62 +information on how to free up more memory for DOOM. + +information on how to free up more memory for Chex(R) Quest. + + + +Text 14 23 +DOOM aborted. +Chex(R) Quest aborted. + + +Text 440 104 +Once you beat the big badasses and +clean out the moon base you're supposed +to win, aren't you? Aren't you? Where's +your fat reward and ticket home? What +the hell is this? It's not supposed to +end this way! + +It stinks like rotten meat, but looks +like the lost Deimos base. Looks like +you're stuck on The Shores of Hell. +The only way out is through. + +To continue the DOOM experience, play +The Shores of Hell and its amazing +sequel, Inferno! +Mission accomplished. + +Are you prepared for the next mission? + + + + + + +Press the escape key to continue... + + +Text 466 15 +You've done it! The hideous cyber- +demon lord that ruled the lost Deimos +moon base has been slain and you +are triumphant! But ... where are +you? You clamber to the edge of the +moon and look down to see the awful +truth. + +Deimos floats above Hell itself! +You've never heard of anyone escaping +from Hell, but you'll make the bastards +sorry they ever heard of you! Quickly, +you rappel down to the surface of +Hell. + +Now, it's on to the final chapter of +DOOM! -- Inferno.You've done it! + +Text 492 14 +The loathsome spiderdemon that +masterminded the invasion of the moon +bases and caused so much death has had +its ass kicked for all time. + +A hidden doorway opens and you enter. +You've proven too tough for Hell to +contain, and now Hell at last plays +fair -- for you emerge from the door +to see the green fields of Earth! +Home at last. + +You wonder what's been happening on +Earth while you were battling evil +unleashed. It's good that no Hell- +spawn could have come through that +door with you ...Wonderful Job! + +Text 503 9 +the spider mastermind must have sent forth +its legions of hellspawn before your +final confrontation with that terrible +beast from hell. but you stepped forward +and brought forth eternal damnation and +suffering upon the horde as a true hero +would in the face of something so evil. + +besides, someone was gonna pay for what +happened to daisy, your pet rabbit. + +but now, you see spread before you more +potential pain and gibbitude as a nation +of demons run amok among our cities. + +next stop, hell on earth!Fantastic + +Text 405 6 +YOU HAVE ENTERED DEEPLY INTO THE INFESTED +STARPORT. BUT SOMETHING IS WRONG. THE +MONSTERS HAVE BROUGHT THEIR OWN REALITY +WITH THEM, AND THE STARPORT'S TECHNOLOGY +IS BEING SUBVERTED BY THEIR PRESENCE. + +AHEAD, YOU SEE AN OUTPOST OF HELL, A +FORTIFIED ZONE. IF YOU CAN GET PAST IT, +YOU CAN PENETRATE INTO THE HAUNTED HEART +OF THE STARBASE AND FIND THE CONTROLLING +SWITCH WHICH HOLDS EARTH'S POPULATION +HOSTAGE.Great! + +Text 617 10 +YOU HAVE WON! YOUR VICTORY HAS ENABLED +HUMANKIND TO EVACUATE EARTH AND ESCAPE +THE NIGHTMARE. NOW YOU ARE THE ONLY +HUMAN LEFT ON THE FACE OF THE PLANET. +CANNIBAL MUTATIONS, CARNIVOROUS ALIENS, +AND EVIL SPIRITS ARE YOUR ONLY NEIGHBORS. +YOU SIT BACK AND WAIT FOR DEATH, CONTENT +THAT YOU HAVE SAVED YOUR SPECIES. + +BUT THEN, EARTH CONTROL BEAMS DOWN A +MESSAGE FROM SPACE: "SENSORS HAVE LOCATED +THE SOURCE OF THE ALIEN INVASION. IF YOU +GO THERE, YOU MAY BE ABLE TO BLOCK THEIR +ENTRY. THE ALIEN BASE IS IN THE HEART OF +YOUR OWN HOME CITY, NOT FAR FROM THE +STARPORT." SLOWLY AND PAINFULLY YOU GET +UP AND RETURN TO THE FRAY.Way to go! + +Text 312 20 +YOU ARE AT THE CORRUPT HEART OF THE CITY, +SURROUNDED BY THE CORPSES OF YOUR ENEMIES. +YOU SEE NO WAY TO DESTROY THE CREATURES' +ENTRYWAY ON THIS SIDE, SO YOU CLENCH YOUR +TEETH AND PLUNGE THROUGH IT. + +THERE MUST BE A WAY TO CLOSE IT ON THE +OTHER SIDE. WHAT DO YOU CARE IF YOU'VE +GOT TO GO THROUGH HELL TO GET TO IT?Thanks for the help! + +Text 494 7 +THE HORRENDOUS VISAGE OF THE BIGGEST +DEMON YOU'VE EVER SEEN CRUMBLES BEFORE +YOU, AFTER YOU PUMP YOUR ROCKETS INTO +HIS EXPOSED BRAIN. THE MONSTER SHRIVELS +UP AND DIES, ITS THRASHING LIMBS +DEVASTATING UNTOLD MILES OF HELL'S +SURFACE. + +YOU'VE DONE IT. THE INVASION IS OVER. +EARTH IS SAVED. HELL IS A WRECK. YOU +WONDER WHERE BAD FOLKS WILL GO WHEN THEY +DIE, NOW. WIPING THE SWEAT FROM YOUR +FOREHEAD YOU BEGIN THE LONG TREK BACK +HOME. REBUILDING EARTH OUGHT TO BE A +LOT MORE FUN THAN RUINING IT WAS. +Great! + + +Text 164 9 +CONGRATULATIONS, YOU'VE FOUND THE SECRET +LEVEL! LOOKS LIKE IT'S BEEN BUILT BY +HUMANS, RATHER THAN DEMONS. YOU WONDER +WHO THE INMATES OF THIS CORNER OF HELL +WILL BE.Fabulous! + +Text 92 17 +CONGRATULATIONS, YOU'VE FOUND THE +SUPER SECRET LEVEL! YOU'D BETTER +BLAZE THROUGH THIS ONE! +CONGRATULATIONS! + + +Text 433 12 +You gloat over the steaming carcass of the +Guardian. With its death, you've wrested +the Accelerator from the stinking claws +of Hell. You relax and glance around the +room. Damn! There was supposed to be at +least one working prototype, but you can't +see it. The demons must have taken it. + +You must find the prototype, or all your +struggles will have been wasted. Keep +moving, keep fighting, keep killing. +Oh yes, keep living, too.Nicely done! + +Text 193 9 +Even the deadly Arch-Vile labyrinth could +not stop you, and you've gotten to the +prototype Accelerator which is soon +efficiently and permanently deactivated. + +You're good at that kind of thing.Nice Job! + +Text 328 10 +You've bashed and battered your way into +the heart of the devil-hive. Time for a +Search-and-Destroy mission, aimed at the +Gatekeeper, whose foul offspring is +cascading to Earth. Yeah, he's bad. But +you know who's worse! + +Grinning evilly, you check your gear, and +get ready to give the bastard a little Hell +of your own making!Well done! + +Text 460 12 +The Gatekeeper's evil face is splattered +all over the place. As its tattered corpse +collapses, an inverted Gate forms and +sucks down the shards of the last +prototype Accelerator, not to mention the +few remaining demons. You're done. Hell +has gone back to pounding bad dead folks +instead of good live ones. Remember to +tell your grandkids to put a rocket +launcher in your coffin. If you go to Hell +when you die, you'll need it for some +final cleaning-up ...Eat Chex(R)! + +Text 159 14 +You've found the second-hardest level we +got. Hope you have a saved game a level or +two previous. If not, be prepared to die +aplenty. For master marines only.Are you ready? + +Text 106 15 +Betcha wondered just what WAS the hardest +level we had ready for ya? Now you know. +No one gets out alive.Were you ready? + +Text 389 23 +You've fought your way out of the infested +experimental labs. It seems that UAC has +once again gulped it down. With their +high turnover, it must be hard for poor +old UAC to buy corporate health insurance +nowadays.. + +Ahead lies the military complex, now +swarming with diseased horrors hot to get +their teeth into you. With luck, the +complex still has some warlike ordnance +laying around.There's more to come... + +Text 310 22 +You hear the grinding of heavy machinery +ahead. You sure hope they're not stamping +out new hellspawn, but you're ready to +ream out a whole herd if you have to. +They might be planning a blood feast, but +you feel about as mean as two thousand +maniacs packed into one mad killer. + +You don't plan to go down easy.Keep up the good work! + +Text 309 11 +The vista opening ahead looks real damn +familiar. Smells familiar, too -- like +fried excrement. You didn't like this +place before, and you sure as hell ain't +planning to like it now. The more you +brood on it, the madder you get. +Hefting your gun, an evil grin trickles +onto your face. Time to take some names.Get ready!. + +Text 385 9 +Suddenly, all is silent, from one horizon +to the other. The agonizing echo of Hell +fades away, the nightmare sky turns to +blue, the heaps of monster corpses start +to evaporate along with the evil stench +that filled the air. Jeeze, maybe you've +done it. Have you really won? + +Something rumbles in the distance. +A blue light begins to glow inside the +ruined skull of the demon-spitter.Be Proud. + +Text 173 4 +What now? Looks totally different. Kind +of like King Tut's condo. Well, +whatever's here can't be any worse +than usual. Can it? Or maybe it's best +to let sleeping gods lie..Wow! + +Text 353 6 +Time for a vacation. You've burst the +bowels of hell and by golly you're ready +for a break. You mutter to yourself, +Maybe someone else can kick Hell's ass +next time around. Ahead lies a quiet town, +with peaceful flowing water, quaint +buildings, and presumably no Hellspawn. + +As you step off the transport, you hear +the stomp of a cyberdemon's iron shoe.Great. + +Text 9 18 +ZOMBIEMANFLEMOIDUS COMMONUS + +Text 11 19 +SHOTGUN GUYFLEMOIDUS BIPEDICUS + +Text 5 30 +DEMONFLEMOIDUS BIPEDICUS WITH ARMOR + +Text 13 13 +BARON OF HELLTHE FLEMBRANE + +Text 81 70 + The Ultimate DOOM Startup v%i.%i Chex(R) Quest Startup + +Text 80 59 + DOOM 2: Plutonia Experiment v%i.%i Chex(R) Quest + +Text 78 61 + DOOM 2: TNT - Evilution v%i.%i Chex(R) Quest + +Text 80 65 + DOOM 2: Hell on Earth v%i.%i Chex(R) Quest + +Text 55 55 + +You cannot -file with the shareware version. Register! +You cannot -file with Chex(R) Quest version. Register! + +Text 36 23 + +This is not the registered version. +This is Chex(R) Quest. + +Text 415 0 +=========================================================================== +ATTENTION: This version of DOOM has been modified. If you would like to +get a copy of the original game, call 1-800-IDGAMES or see the readme file. + You will not receive technical support for modified games. + press enter to continue +=========================================================================== + + +Text 281 0 +=========================================================================== + This version is NOT SHAREWARE, do not distribute! + Please report software piracy to the SPA: 1-800-388-PIR8 +=========================================================================== + + +Text 265 0 +=========================================================================== + Do not distribute! + Please report software piracy to the SPA: 1-800-388-PIR8 +=========================================================================== + + +Text 35 44 +R_Init: Init DOOM refresh daemon - R_Init: Init Chex(R) Quest refresh daemon - + +Text 25 25 +c:\doomdata\doomsav%c.dsgc:\doomdata\chexsav%c.dsg + +Text 13 13 +doomsav%c.dsgchexsav%c.dsg + +Text 47 57 +Different DOOM versions cannot play a net game!Different Chex(R) Quest versions cannot play a net quest! + +Text 25 25 +c:\doomdata\doomsav%d.dsgc:\doomdata\chexsav%d.dsg + +Text 13 13 +doomsav%d.dsgchexsav%d.dsg + +Text 25 25 +c:\doomdata\doomsav%d.dsgc:\doomdata\chexsav%d.dsg + +Text 13 13 +doomsav%d.dsgchexsav%d.dsg + +Text 52 53 +you can't do load while in a net game! + +press a key.you can't do load while in a net quest! + +press a key. + +Text 52 53 +quicksave over your game named + +'%s'? + +press y or n.quicksave over your quest named + +'%s'? + +press y or n. + +Text 51 52 +you can't quickload during a netgame! + +press a key.you can't quickload during a netquest! + +press a key. + +Text 61 62 +do you want to quickload the game named + +'%s'? + +press y or n.do you want to quickload the quest named + +'%s'? + +press y or n. + +Text 65 67 +you can't start a new game +while in a network game. + +press a key.you can't start a new quest +while in a network quest. + +press a key. + +Text 71 68 +are you sure? this skill level +isn't even remotely fair. + +press y or n.Careful, this will be tough. +Do you wish to continue? + +press y or n. + +Text 91 82 +this is the shareware version of doom. + +you need to order the entire trilogy. + +press a key.this is Chex(R) Quest. look for + +future levels at www.chexquest.com. + +press a key. + +Text 38 39 +you can't end a netgame! + +press a key.you can't end a netquest! + +press a key. + +Text 53 54 +are you sure you want to end the game? + +press y or n.are you sure you want to end the quest? + +press y or n. + +Text 23 19 +I'm ready to kick butt!I'm ready to zorch! + +Text 7 18 +I'm OK.I'm feeling great! + +Text 25 28 +I'm not looking too good!I'm getting pretty gooed up! + +Text 5 17 +Help!Somebody help me! + +Text 9 30 +You suck!Go back to your own dimension! + +Text 21 17 +Next time, scumbag...Stop that Flemoid + +Text 10 17 +Come here!I think I'm lost! + +Text 21 30 +I'll take care of it.I'll get you out of this gunk. + +Text 10 10 +DOOM00.pcxCHEX00.pcx + +Text 20 28 +Picked up the armor.Picked up the Chex(R) Armor. + +Text 24 34 +Picked up the MegaArmor!Picked up the Super Chex(R) Armor! + +Text 25 27 +Picked up a health bonus.Picked up a glass of water. + +Text 25 26 +Picked up an armor bonus.Picked up slime repellent. + +Text 12 22 +Supercharge!Supercharge Breakfast! + +Text 25 21 +Picked up a blue keycard.Picked up a blue key. + +Text 27 23 +Picked up a yellow keycard.Picked up a yellow key. + +Text 24 20 +Picked up a red keycard.Picked up a red key. + +Text 21 26 +Picked up a stimpack.Picked up a bowl of fruit. + +Text 41 33 +Picked up a medikit that you REALLY need!Picked up some needed vegetables! + +Text 20 31 +Picked up a medikit.Picked up a bowl of vegetables. + +Text 24 15 +Radiation Shielding SuitSlimeproof Suit + +Text 17 32 +Picked up a clip.Picked up a mini zorch recharge. + +Text 27 28 +Picked up a box of bullets.Picked up a mini zorch pack. + +Text 19 37 +Picked up a rocket.Picked up a zorch propulsor recharge. + +Text 27 33 +Picked up a box of rockets.Picked up a zorch propulsor pack. + +Text 25 37 +Picked up an energy cell.Picked up a phasing zorcher recharge. + +Text 30 33 +Picked up an energy cell pack.Picked up a phasing zorcher pack. + +Text 27 35 +Picked up 4 shotgun shells.Picked up a large zorcher recharge. + +Text 34 31 +Picked up a box of shotgun shells.Picked up a large zorcher pack. + +Text 34 21 +Picked up a backpack full of ammo!Picked up a Zorchpak! + +Text 30 23 +You got the BFG9000! Oh, yes.You got the LAZ Device! + +Text 21 26 +You got the chaingun!You got the Rapid Zorcher! + +Text 28 28 +A chainsaw! Find some meat!You got the Super Bootspork! + +Text 28 28 +You got the rocket launcher!You got the Zorch Propulsor! + +Text 23 28 +You got the plasma gun!You got the Phasing Zorcher! + +Text 20 26 +You got the shotgun!You got the Large Zorcher! + +Text 26 32 +You got the super shotgun!You got the Super Large Zorcher! + +Text 22 18 +Degreelessness Mode OnInvincible Mode On + +Text 23 19 +Degreelessness Mode OffInvincible Mode Off + +Text 20 11 +Ammo (no keys) AddedZorch Added + +Text 21 17 +Very Happy Ammo AddedSuper Zorch Added + +Text 21 16 +... doesn't suck - GM... Eat Chex(R)! + +Text 23 19 +I'm ready to kick butt!I'm ready to zorch! + +Text 7 18 +I'm OK.I'm feeling great! + +Text 25 28 +I'm not looking too good!I'm getting pretty gooed up! + +Text 5 17 +Help!Somebody help me! + +Text 9 30 +You suck!Go back to your own dimension! + +Text 21 17 +Next time, scumbag...Stop that Flemoid + +Text 10 17 +Come here!I think I'm lost! + +Text 21 30 +I'll take care of it.I'll get you out of this gunk. + +Text 12 18 +E1M1: HangarE1M1: Landing Zone + +Text 19 22 +E1M2: Nuclear PlantE1M2: Storage Facility + +Text 20 22 +E1M3: Toxin RefineryE1M3: Experimental Lab + +Text 21 15 +E1M4: Command ControlE1M4: Arboretum + +Text 16 23 +E1M5: Phobos LabE1M5: Caverns of Bazoik + +Text 22 18 +You mumble to yourselfI'm feeling great. + +Text 12 17 +Who's there?I think I'm lost. + +Text 18 8 +You scare yourselfOh No... + +Text 17 17 +You start to raveGotta break free. + +Text 17 6 +You've lost it...Hurry! + +# Frame/Thing table changes: + +Frame 537 +Duration = 3 + +Frame 538 +Duration = 3 + +Frame 539 +Duration = 0 + +Thing 2 (Zombieman) +Close attack frame = 184 +Far attack frame = 0 + +Thing 3 (Shotgun guy) +Close attack frame = 217 +Far attack frame = 0 + +Thing 16 (Boss) +Width = 2883584 +Height = 6553600 +Speed = 0 + +Thing 19 +Height = 0 + +# Cheats: + +Cheat 0 +Chainsaw = joelkoenigs +God mode = davidbrus +Ammo & Keys = scottholman +Ammo = mikekoenigs +No Clipping 1 = charlesjacobi +No Clipping 2 = charlesjacobi +Invincibility = andrewbenson +Berserk = deanhyers +Invisibility = marybregi +Radiation Suit = allen +Auto-map = digitalcafe +Lite-amp Goggles = joshuastorms +Level Warp = leesnyder +Player Position = kimhyers +Map cheat = sherrill + diff --git a/data/lumps/crblack.lmp b/data/lumps/crblack.lmp new file mode 100644 index 0000000..5861c1d Binary files /dev/null and b/data/lumps/crblack.lmp differ diff --git a/data/lumps/crblue.lmp b/data/lumps/crblue.lmp new file mode 100644 index 0000000..d9bcd01 Binary files /dev/null and b/data/lumps/crblue.lmp differ diff --git a/data/lumps/crblue2.lmp b/data/lumps/crblue2.lmp new file mode 100644 index 0000000..3c19d5b Binary files /dev/null and b/data/lumps/crblue2.lmp differ diff --git a/data/lumps/crbrick.lmp b/data/lumps/crbrick.lmp new file mode 100644 index 0000000..ed4f68b Binary files /dev/null and b/data/lumps/crbrick.lmp differ diff --git a/data/lumps/crbrown.lmp b/data/lumps/crbrown.lmp new file mode 100644 index 0000000..a47d222 Binary files /dev/null and b/data/lumps/crbrown.lmp differ diff --git a/data/lumps/crgold.lmp b/data/lumps/crgold.lmp new file mode 100644 index 0000000..8a6d530 Binary files /dev/null and b/data/lumps/crgold.lmp differ diff --git a/data/lumps/crgray.lmp b/data/lumps/crgray.lmp new file mode 100644 index 0000000..384f2d4 Binary files /dev/null and b/data/lumps/crgray.lmp differ diff --git a/data/lumps/crgreen.lmp b/data/lumps/crgreen.lmp new file mode 100644 index 0000000..d991b4e Binary files /dev/null and b/data/lumps/crgreen.lmp differ diff --git a/data/lumps/crorange.lmp b/data/lumps/crorange.lmp new file mode 100644 index 0000000..f9a4d16 Binary files /dev/null and b/data/lumps/crorange.lmp differ diff --git a/data/lumps/crpurple.lmp b/data/lumps/crpurple.lmp new file mode 100644 index 0000000..1a5a78a Binary files /dev/null and b/data/lumps/crpurple.lmp differ diff --git a/data/lumps/crred.lmp b/data/lumps/crred.lmp new file mode 100644 index 0000000..79d0e57 Binary files /dev/null and b/data/lumps/crred.lmp differ diff --git a/data/lumps/crtan.lmp b/data/lumps/crtan.lmp new file mode 100644 index 0000000..08d070f Binary files /dev/null and b/data/lumps/crtan.lmp differ diff --git a/data/lumps/crwhite.lmp b/data/lumps/crwhite.lmp new file mode 100644 index 0000000..825ee3b Binary files /dev/null and b/data/lumps/crwhite.lmp differ diff --git a/data/lumps/cryellow.lmp b/data/lumps/cryellow.lmp new file mode 100644 index 0000000..edcdca7 Binary files /dev/null and b/data/lumps/cryellow.lmp differ diff --git a/data/lumps/endboom.lmp b/data/lumps/endboom.lmp new file mode 100644 index 0000000..62805b9 --- /dev/null +++ b/data/lumps/endboom.lmp @@ -0,0 +1 @@ +                                                                                       @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @BO OOO OOO OMO O-O OaO ODOOOOOMO OeOnOgOiOnOeO OmOoOdOiOfOiOcOaOtOiOoOnO @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @     @ @ @ @ @ @ @ @ @ @DKeKsKiKgKnKeKdK,K KiKmKpKlKeKmKeKnKtKeKdK KaKnKdK KpKrKeKsKeKnKtKeKdK KtKoK KyKoKuK KbKyK KTKeKaKmKTKNKTK K @ @ @ @ @ @ @ @                                                                                     This is the Phase I release of the BOOM executable, a DOOM source code      modification by TeamTNT.  Please review the text files for information       about how to play with and edit for the new engine features.                                                                                                          (C) Copyright 1998, TeamTNT - All rights reserved                                                                                                                           Thanks for playing!                                                                                                                DOOM is Copyright (C) id Software and the source is modified with              their permission based on the public release in December 1997.                                                                                                 Keep up with TeamTNT goings on.  See our web page at:                                      <http://www.teamtnt.com>                                                                                                                                                                                                \ No newline at end of file diff --git a/data/lumps/gammatbl.lmp b/data/lumps/gammatbl.lmp new file mode 100644 index 0000000..27f345f --- /dev/null +++ b/data/lumps/gammatbl.lmp @@ -0,0 +1,3 @@ + +  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ +  !"$%&'()*,-./012346789:;<=>?@ABCEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !#$&'(*+-./02346789;<=>?ABCDEFHIJKLMNOPRSTUVWXYZ[\]^_`abdefghijklmnopqrrstuvwxyz{|}~ "$&()+-/124579:<=?@ACDFGHJKLMOPQRTUVWXZ[\]^_`bcdefghijklmnopqrstuvwxyz{|}~ $'*-02579<>@BDEGIKLNPQSTVWYZ\]^`abdefgijklmnpqrstuvwxyz{|}~ \ No newline at end of file diff --git a/data/lumps/glfp.lmp b/data/lumps/glfp.lmp new file mode 100644 index 0000000..f76efd2 --- /dev/null +++ b/data/lumps/glfp.lmp @@ -0,0 +1,18 @@ +uniform sampler2D tex; +uniform float lightlevel; + +#define DOOMLIGHTFACTOR 232.0 + +void main() +{ + vec4 color = gl_Color; + + // Doom lighting equation ripped from EDGE. + float L = 63.0 * lightlevel; + float min_L = clamp(36.0 - L, 0.0, 31.0); + float dist = max(0.0, gl_FragCoord.z); + float index = 59.0 + DOOMLIGHTFACTOR - L - DOOMLIGHTFACTOR / dist; + color.rgb *= (1.0 - clamp(index, min_L, 31.0) / 31.0); + + gl_FragColor = color * texture2D(tex, gl_TexCoord[0].st); +} diff --git a/data/lumps/glshadow.lmp b/data/lumps/glshadow.lmp new file mode 100644 index 0000000..5c83bc8 Binary files /dev/null and b/data/lumps/glshadow.lmp differ diff --git a/data/lumps/glvp.lmp b/data/lumps/glvp.lmp new file mode 100644 index 0000000..7e9eec1 --- /dev/null +++ b/data/lumps/glvp.lmp @@ -0,0 +1,6 @@ +void main() +{ + gl_FrontColor = gl_Color; + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + gl_Position = ftransform(); +} diff --git a/data/lumps/m_ammo.lmp b/data/lumps/m_ammo.lmp new file mode 100644 index 0000000..251abfe Binary files /dev/null and b/data/lumps/m_ammo.lmp differ diff --git a/data/lumps/m_armour.lmp b/data/lumps/m_armour.lmp new file mode 100644 index 0000000..e642060 Binary files /dev/null and b/data/lumps/m_armour.lmp differ diff --git a/data/lumps/m_arrow.lmp b/data/lumps/m_arrow.lmp new file mode 100644 index 0000000..69390e5 Binary files /dev/null and b/data/lumps/m_arrow.lmp differ diff --git a/data/lumps/m_health.lmp b/data/lumps/m_health.lmp new file mode 100644 index 0000000..f25a375 Binary files /dev/null and b/data/lumps/m_health.lmp differ diff --git a/data/lumps/m_key.lmp b/data/lumps/m_key.lmp new file mode 100644 index 0000000..cb7faa4 Binary files /dev/null and b/data/lumps/m_key.lmp differ diff --git a/data/lumps/m_mark.lmp b/data/lumps/m_mark.lmp new file mode 100644 index 0000000..79c2ea6 Binary files /dev/null and b/data/lumps/m_mark.lmp differ diff --git a/data/lumps/m_normal.lmp b/data/lumps/m_normal.lmp new file mode 100644 index 0000000..a81a7e6 Binary files /dev/null and b/data/lumps/m_normal.lmp differ diff --git a/data/lumps/m_power.lmp b/data/lumps/m_power.lmp new file mode 100644 index 0000000..582e9ea Binary files /dev/null and b/data/lumps/m_power.lmp differ diff --git a/data/lumps/m_shadow.lmp b/data/lumps/m_shadow.lmp new file mode 100644 index 0000000..40c1258 Binary files /dev/null and b/data/lumps/m_shadow.lmp differ diff --git a/data/lumps/m_weap.lmp b/data/lumps/m_weap.lmp new file mode 100644 index 0000000..a1165bb Binary files /dev/null and b/data/lumps/m_weap.lmp differ diff --git a/data/lumps/nervebex.lmp b/data/lumps/nervebex.lmp new file mode 100644 index 0000000..1fc53e1 --- /dev/null +++ b/data/lumps/nervebex.lmp @@ -0,0 +1,33 @@ +[STRINGS] +HUSTR_1 = level 1: the earth base +HUSTR_2 = level 2: the pain labs +HUSTR_3 = level 3: canyon of the dead +HUSTR_4 = level 4: hell mountain +HUSTR_5 = level 5: vivisection +HUSTR_6 = level 6: inferno of blood +HUSTR_7 = level 7: baron's banquet +HUSTR_8 = level 8: tomb of malevolence +HUSTR_9 = level 9: march of the demons +C6TEXT = trouble was brewing again in your favorite\nvacation spot... hell. some cyberdemon\npunk thought he could turn hell into a\npersonal amusement park, and make earth\nthe ticket booth.\n\nwell that half-robot freak show didn't\nknow who was coming to the fair. there's\nnothing like a shooting gallery full of\nhellspawn to get the blood pumping...\n\nnow the walls of the demon's labyrinth\necho with the sound of his metallic limbs\nhitting the floor. his death moan gurgles\nout through the mess you left of his face.\n\nthis ride is closed. + +[PARS] +par 1 75 +par 2 105 +par 3 120 +par 4 105 +par 5 210 +par 6 105 +par 7 165 +par 8 105 +par 9 135 + +[MUSIC] +runnin = messag +stalks = ddtblu +countd = doom +betwee = shawn +doom = in_cit +#the_da = the_da +shawn = in_cit +ddtblu = shawn2 +in_cit = ddtbl2 diff --git a/data/lumps/sinetabl.lmp b/data/lumps/sinetabl.lmp new file mode 100644 index 0000000..d190722 Binary files /dev/null and b/data/lumps/sinetabl.lmp differ diff --git a/data/lumps/switches.lmp b/data/lumps/switches.lmp new file mode 100644 index 0000000..fda1ecf Binary files /dev/null and b/data/lumps/switches.lmp differ diff --git a/data/lumps/tangtabl.lmp b/data/lumps/tangtabl.lmp new file mode 100644 index 0000000..c31dcf8 Binary files /dev/null and b/data/lumps/tangtabl.lmp differ diff --git a/data/lumps/tantoang.lmp b/data/lumps/tantoang.lmp new file mode 100644 index 0000000..2d97fb7 Binary files /dev/null and b/data/lumps/tantoang.lmp differ diff --git a/data/lumps/watermap.lmp b/data/lumps/watermap.lmp new file mode 100644 index 0000000..1b62be9 Binary files /dev/null and b/data/lumps/watermap.lmp differ diff --git a/data/palette.rgb b/data/palette.rgb new file mode 100644 index 0000000..8946a52 Binary files /dev/null and b/data/palette.rgb differ diff --git a/data/prboom.txt b/data/prboom.txt new file mode 100644 index 0000000..80790e5 --- /dev/null +++ b/data/prboom.txt @@ -0,0 +1,229 @@ +# PWAD creation directives for prboom.wad +# Initially generated by DeuTex 4.4.0 + +# List of Floors and Ceilings +[flats] +-N0_TEX- + +# List of data Lumps +[lumps] +SWITCHES +ANIMATED +CRBRICK +CRTAN +CRGRAY +CRGREEN +CRBROWN +CRGOLD +CRRED +CRBLUE +CRBLUE2 +CRORANGE +CRYELLOW + +C_START +WATERMAP +C_END + +# PrBoom internal data lumps (large data lumps removed from the source code) +B_START +SINETABL +TANGTABL +TANTOANG +GAMMATBL +CHEXDEH +BFGBEX +NERVEBEX +GLSHADOW +GLFP +GLVP +-PRBHUD- +M_AMMO +M_ARMOUR +M_ARROW +M_HEALTH +M_KEY +M_NORMAL +M_SHADOW +M_POWER +M_WEAP +M_MARK +B_END + +# List of Sounds +[sounds] +# MBF dog sounds +DSDGSIT +DSDGATK +DSDGACT +DSDGDTH +DSDGPAIN +DSSECRET +DSGIBDTH + +# List of Pictures (with insertion point) +[graphics] +DIG0 0 0 +DIG1 0 0 +DIG2 0 0 +DIG3 0 0 +DIG4 0 0 +DIG5 0 0 +DIG6 0 0 +DIG7 0 0 +DIG8 0 0 +DIG9 0 0 +DIGA 0 0 +DIGB 0 0 +DIGC 0 0 +DIGD 0 0 +DIGE 0 0 +DIGF 0 0 +DIGG 0 0 +DIGH 0 0 +DIGI 0 0 +DIGJ 0 0 +DIGK 0 0 +DIGL 0 0 +DIGM 0 0 +DIGN 0 0 +DIGO 0 0 +DIGP 0 0 +DIGQ 0 0 +DIGR 0 0 +DIGS 0 0 +DIGT 0 0 +DIGU 0 0 +DIGV 0 0 +DIGW 0 0 +DIGX 0 0 +DIGY 0 0 +DIGZ 0 0 +DIG45 0 0 +DIG47 0 0 +DIG58 0 0 +DIG91 0 0 +DIG93 0 0 +STBR123 0 0 +STBR124 0 0 +STBR125 0 0 +STBR126 0 0 +STBR127 0 0 +BOXUL 0 0 +BOXUC 0 0 +BOXUR 0 0 +BOXCL 0 0 +BOXCC 0 0 +BOXCR 0 0 +BOXLL 0 0 +BOXLC 0 0 +BOXLR 0 0 +STKEYS6 0 0 +STKEYS7 0 0 +STKEYS8 0 0 +STCFN096 0 0 +M_BUTT1 0 0 +M_BUTT2 0 0 +M_COLORS 0 0 +M_PALNO 0 0 +M_PALSEL 0 0 +M_VBOX 0 0 +M_GENERL 0 0 +M_SETUP 0 0 +M_AUTO 0 0 +M_KEYBND 0 0 +M_CHAT 0 0 +M_COMPAT 0 0 +M_ENEM 0 0 +M_MESS 0 0 +M_STAT 0 0 +M_WEAP 0 0 +M_HORSEN 0 0 +M_VERSEN 0 0 +PRBOOM 0 0 +M_ACCEL 0 0 +M_LOKSEN 0 0 +CROSS1 0 0 +CROSS2 0 0 +CROSS3 0 0 + +# List of Sprites +[sprites] +# Empty sprite +TNT1A0 0 0 + +# MBF dog sprites +DOGSD5 32 59 +DOGSH5 32 59 +DOGSC5 32 59 +DOGSG5 32 59 +DOGSB1 32 59 +DOGSA5 32 59 +DOGSE5 32 59 +DOGSC1 32 59 +DOGSD1 32 59 +DOGSB5 32 59 +DOGSF5 32 59 +DOGSA1 32 59 +DOGSE1 32 59 +DOGSF1 32 59 +DOGSD2D8 32 59 +DOGSH2 32 59 +DOGSG1 32 59 +DOGSH8 32 59 +DOGSE8 32 59 +DOGSD4D6 32 59 +DOGSH4 32 59 +DOGSA2A8 32 59 +DOGSE2 32 59 +DOGSF8 32 59 +DOGSH1 32 59 +DOGSA4A6 32 59 +DOGSE4 32 59 +DOGSB4B6 32 59 +DOGSF4 32 59 +DOGSH6 32 59 +DOGSB2B8 32 59 +DOGSF2 32 59 +DOGSC2C8 32 59 +DOGSG2 32 59 +DOGSG6 32 59 +DOGSC4C6 32 59 +DOGSG4 32 59 +DOGSG8 32 59 +DOGSF6 32 59 +DOGSN0 32 59 +DOGSE6 32 59 +DOGSD3D7 32 59 +DOGSH3 32 59 +DOGSH7 32 59 +DOGSI0 32 59 +DOGSA3A7 32 59 +DOGSE3 32 59 +DOGSB3B7 32 59 +DOGSF3 32 59 +DOGSF7 32 59 +DOGSE7 32 59 +DOGSC3C7 32 59 +DOGSG3 32 59 +DOGSG7 32 59 +DOGSJ0 32 59 +DOGSK0 32 59 +DOGSL0 32 59 +DOGSM0 32 59 + +# MBF -beta sprites +PLS1A0 8 12 +PLS1B0 9 12 +PLS1C0 8 12 +PLS1D0 9 12 +PLS1E0 25 37 +PLS1F0 25 37 +PLS1G0 26 42 +PLS2A0 7 10 +PLS2B0 6 8 +PLS2C0 5 6 +PLS2D0 7 10 +PLS2E0 11 16 + +# End of extraction diff --git a/data/rd_graphic.c b/data/rd_graphic.c new file mode 100644 index 0000000..76bb4f6 --- /dev/null +++ b/data/rd_graphic.c @@ -0,0 +1,197 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Convert portable pixmap to Doom patch format + +#include "config.h" + +#include +#include +#include +#include + +#include "rd_util.h" +#include "rd_palette.h" +#include "rd_graphic.h" + +// +// parseppm +// +// try to read a ppm file header and get width/height of image +// + +static unsigned char *parseppm(char *ppm, size_t size, const char *file, + int *width, int *height) +{ + int maxcol, numpixels; + char *pos = ppm; + + if (size < 2 || !(*pos++ == 'P' && *pos++ == '6')) + die("Not a PPM: %s\n", file); + + // Ignore comments like "# Created by GIMP ..." in line 2 + if (*pos++ == '\n' && *pos == '#') + while (*pos != '\n') pos++; + + numpixels = *width = strtol(pos, &pos, 0); + numpixels *= *height = strtol(pos, &pos, 0); + maxcol = strtol(pos, &pos, 0); + + if (maxcol != 255 || !isspace(*pos++)) + die("Invalid PPM header: %s\n", file); + + if (!numpixels || numpixels > (size-(ppm-pos))/3) + die("Invalid PPM size: %s\n", file); + + return (unsigned char *)pos; +} + +// +// column_pixels_to_colours +// +// make an array of colour indices from a column of raw pixel data +// (random access to which is easier while building a patch column) +// + +static void pixels_to_colours(int *colours, unsigned char *pixels, + int width, int height, int x) +{ + int i = height; + int *colour = colours; + unsigned char *rgb = &pixels[3*x]; + + while (--i>=0) + { + *colour = palette_getindex(rgb); + colour++; + rgb += 3*width; + } +} + +// +// createcolumn +// +// make a doom patch column from an array of colour indices +// + +static size_t createcolumn(unsigned char **column, int *colours, int height) +{ + size_t size = 256, length = 0; + unsigned char *data = xmalloc(size); + int i, y, top, transparent, opaque; + + for (y = 0; y < height; ) + { + // we are at the start of a new post + top = y; + + // count transparent pixels above post and opaque pixels in it + transparent = opaque = 0; + for (; y < height && colours[y] == -1; y++) transparent++; + for (; y < height && colours[y] >= 0; y++) opaque++; + + if (opaque > 0) // this post has pixels + { + while (size < length + opaque + 8) + size *= 2, data = xrealloc(data, size); + + data[length++] = top + transparent; // column offset + data[length++] = opaque; // length of column + data[length++] = 0; // padding + + for (i = 0; i < opaque; i++) + data[length++] = colours[top + transparent + i]; + + data[length++] = 0; // more padding + } + } + data[length++] = 0xff; // end of column + + *column = data; + return length; +} + +// +// ppm_to_patch +// +// convert an 8-bit portable pixmap to doom's patch format +// insert_x and insert_y are the graphic/sprite insertion point +// + +size_t ppm_to_patch(void **lumpdata, const char *filename, + int insert_x, int insert_y) +{ + void *data; + size_t size = read_or_die(&data, filename); + + int i, width, height, *column_colours; + unsigned char *pixels, **columns, *patch; + size_t *columnsizes, totalcolumnsize, offset; + + pixels = parseppm(data, size, filename, &width, &height); + columns = xmalloc(width * sizeof(*columns)); + columnsizes = xmalloc(width * sizeof(*columnsizes)); + column_colours = xmalloc(height * sizeof(*column_colours)); + + for (totalcolumnsize = i = 0; i < width; i++) + { + pixels_to_colours(column_colours, pixels, width, height, i); + columnsizes[i] = createcolumn(&columns[i], column_colours, height); + totalcolumnsize += columnsizes[i]; + } + + patch = xmalloc(8+4*width+totalcolumnsize); + + ((short *)patch)[0] = SHORT(width); + ((short *)patch)[1] = SHORT(height); + ((short *)patch)[2] = SHORT(insert_x); + ((short *)patch)[3] = SHORT(insert_y); + + for (offset = 8+4*width, i = 0; i < width; i++) + { + ((int *)(patch+8))[i] = LONG(offset); + offset += columnsizes[i]; + } + + for (offset = 8+4*width, i = 0; i < width; i++) + { + memmove(patch + offset, columns[i], columnsizes[i]); + offset += columnsizes[i]; + free(columns[i]); + } + + free(column_colours); + free(columnsizes); + free(columns); + free(data); + + *lumpdata = patch; + return 8+4*width+totalcolumnsize; +} + +// +// ppm_to_bitmap +// +// convert an 8-bit PPM to a raw map of Doom palette indices (for flats) +// + +size_t ppm_to_bitmap(void **lumpdata, const char *filename) +{ + void *data; + size_t size = read_or_die(&data, filename); + + int i, j, width, height; + unsigned char *pixels, *bitmap; + + pixels = parseppm(data, size, filename, &width, &height); + bitmap = xmalloc(width * height); + + for (j = 0; j < height; j++) + for (i = 0; i < width; i++) + bitmap[width*j+i] = palette_getindex(&pixels[3*(width*j+i)]); + + free(data); + + *lumpdata = bitmap; + return width * height; +} diff --git a/data/rd_graphic.h b/data/rd_graphic.h new file mode 100644 index 0000000..654d0ef --- /dev/null +++ b/data/rd_graphic.h @@ -0,0 +1,11 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Convert portable pixmap to Doom format + +// convert ppm to doom patch format, with insertion point +size_t ppm_to_patch(void **lumpdata, const char *filename, + int insert_x, int insert_y); + +// convert ppm to raw bitmap (for use with flats) +size_t ppm_to_bitmap(void **lumpdata, const char *filename); diff --git a/data/rd_main.c b/data/rd_main.c new file mode 100644 index 0000000..c6333a1 --- /dev/null +++ b/data/rd_main.c @@ -0,0 +1,174 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Main program, parse command line arguments + +#include "config.h" + +#include +#include +#include +#include + +#include "rd_util.h" +#include "rd_output.h" +#include "rd_sound.h" +#include "rd_palette.h" +#include "rd_graphic.h" + +enum argtype +{ + ARG_NONE, + ARG_OUTPUT, + ARG_INCLUDE, + ARG_PALETTE, + ARG_MARKER, + ARG_LUMP, + ARG_GRAPHIC, + ARG_SOUND, + ARG_FLAT, + ARG_SPRITE, +}; + +static void ATTR((noreturn)) usage(int exitcode) +{ + FILE *f = exitcode ? stderr : stdout; + fprintf(f, "Usage: rdatawad \n" + " -o \n" + " -I \n" + " -palette \n" + " -marker \n" + " -lumps ...\n" + " -graphics ...\n" + " -sounds ...\n" + " -flats ...\n" + " -sprites ...\n"); + exit(exitcode); +} + +int main(int argc, char **argv) +{ + enum argtype argtype = ARG_NONE; + const char *output = NULL; + + if (argc <= 1) + usage(0); + + while (*(++argv)) + { + char *arg = *argv; + + if (*arg == '-') + { + if (*(arg+1) == '-') // allow both -switch and --switch + arg++; + + if (!strcmp(arg, "-o")) + argtype = ARG_OUTPUT; + else if (!strcmp(arg, "-I")) + argtype = ARG_INCLUDE; + else if (!strcmp(arg, "-palette")) + argtype = ARG_PALETTE; + else if (!strcmp(arg, "-marker")) + argtype = ARG_MARKER; + else if (!strcmp(arg, "-lumps")) + argtype = ARG_LUMP; + else if (!strcmp(arg, "-graphics")) + argtype = ARG_GRAPHIC; + else if (!strcmp(arg, "-sounds")) + argtype = ARG_SOUND; + else if (!strcmp(arg, "-flats")) + argtype = ARG_FLAT; + else if (!strcmp(arg, "-sprites")) + argtype = ARG_SPRITE; + else if (!strcmp(arg, "-help") || !strcmp(arg, "-version")) + usage(0); + else + usage(1); + } + else + switch (argtype) + { + case ARG_NONE: + usage(1); + break; + + case ARG_OUTPUT: + output = arg; + argtype = ARG_NONE; + break; + + case ARG_INCLUDE: + search_path(arg); + break; + + case ARG_PALETTE: + palette_init(arg); + argtype = ARG_NONE; + break; + + case ARG_MARKER: + output_add(arg, NULL, 0); + break; + + case ARG_LUMP: + { + void *data = NULL; + size_t size = read_or_die(&data, arg); + output_add(arg, data, size); + } + break; + + case ARG_GRAPHIC: + { + void *data = NULL; + size_t size = ppm_to_patch(&data, arg, 0, 0); + output_add(arg, data, size); + } + break; + + case ARG_SOUND: + { + void *data = NULL; + size_t size = wav_to_doom(&data, arg); + output_add(arg, data, size); + } + break; + + case ARG_FLAT: + { + void *data = NULL; + size_t size = ppm_to_bitmap(&data, arg); + output_add(arg, data, size); + } + break; + + case ARG_SPRITE: + { + void *data = NULL; + char *pos = arg; + size_t size; + int x, y; + + x = strtol(pos, &pos, 0); + if (*pos == ',') pos++; + else if (!isspace(*pos) && !isdigit(*pos)) usage(1); + + y = strtol(pos, &pos, 0); + if (*pos == ',') pos++; + else if (!isspace(*pos) && !isdigit(*pos)) usage(1); + + size = ppm_to_patch(&data, pos, x, y); + output_add(pos, data, size); + } + break; + } + } + + if (output) + output_write(output); + else + die("No output file specified\n"); + + return 0; +} diff --git a/data/rd_output.c b/data/rd_output.c new file mode 100644 index 0000000..c70fc37 --- /dev/null +++ b/data/rd_output.c @@ -0,0 +1,143 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Output wad construction - add lump data, build wad directory + +#include "config.h" + +#include +#include +#include +#include + +#include "rd_util.h" +#include "rd_output.h" + +struct lump +{ + const char *name; + const void *data; + size_t size; + unsigned int offset; +}; + +static unsigned int numlumps, dirsize; +static struct lump *dir; + +// +// extract_lumpname +// + +static char *extract_lumpname(const char *filename) +{ + const char *base; + char *lumpname, *suffix, *c; + + // strip off directory prefix + base = strrchr(filename, '/'); + if (!base) + base = filename; + else + base += 1; + + if (!*base) + die("Empty lumpname: %s\n", filename); + + // copy the name + lumpname = xstrdup(base); + + suffix = strrchr(lumpname, '.'); + if (suffix) + *suffix = '\0'; + + for (c = lumpname; *c; c++) + *c = toupper(*c); + + return lumpname; +} + +// +// output_add - add lump to wad +// + +void output_add(const char *filename, const void *data, size_t size) +{ + struct lump *newlump; + + if (numlumps >= dirsize) + { + dirsize = dirsize ? 2*dirsize : 256; + dir = xrealloc(dir, dirsize * sizeof(*dir)); + } + + newlump = &dir[numlumps++]; + + newlump->name = extract_lumpname(filename); + newlump->data = data; + newlump->size = size; +} + +// +// write* - serialisation functions +// + +// write a uint32_t, byteswapping if necessary +static void write_u32(FILE *f, unsigned int n) +{ + n = LONG(n); + + fwrite(&n, 4, 1, f); +} + +// write a lump name (8 byte string) +static void write_ch8(FILE *f, const char *s) +{ + char buffer[9]; + + memset(buffer, 0, sizeof(buffer)); + snprintf(buffer, sizeof(buffer), "%s", s); + + fwrite(buffer, 8, 1, f); +} + +// +// output_write - write wad to file +// + +void output_write(const char *filename) +{ + unsigned int i; + struct lump *lump; + unsigned int pos = 12; + FILE *out; + + // calculate wad directory offsets + for (i = numlumps, lump = dir; i; i--, lump++) + { + lump->offset = pos; + pos += lump->size; + } + + out = fopen(filename, "wb"); + if (!out) + die("Cannot open %s\n", filename); + + // write wad header + fwrite("PWAD", 4, 1, out); + write_u32(out, numlumps); + write_u32(out, pos); + + // write lumps + for (i = numlumps, lump = dir; i; i--, lump++) + fwrite(lump->data, lump->size, 1, out); + + // write wad directory + for (i = numlumps, lump = dir; i; i--, lump++) + { + write_u32(out, lump->offset); + write_u32(out, lump->size); + write_ch8(out, lump->name); + } + + fclose(out); +} diff --git a/data/rd_output.h b/data/rd_output.h new file mode 100644 index 0000000..bcd6bec --- /dev/null +++ b/data/rd_output.h @@ -0,0 +1,10 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Output wad construction - add lump data, build wad directory + +// append lump to output wad +void output_add(const char *filename, const void *data, size_t size); + +// write output file to filename +void output_write(const char *filename); diff --git a/data/rd_palette.c b/data/rd_palette.c new file mode 100644 index 0000000..25205ca --- /dev/null +++ b/data/rd_palette.c @@ -0,0 +1,93 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Chained hash lookup to convert rgb triples to palette indices + +#include "config.h" + +#include +#include +#include + +#include "rd_util.h" +#include "rd_palette.h" + +static unsigned char *palette_data; + +#define PAL_SIZE 256 + +static struct { + unsigned char rgb[3]; + int first, next; +} hash[PAL_SIZE]; + +#define HASH(c) ((int)((c)[0])+(int)((c)[1])+(int)((c)[2])) + +// +// make_hash +// +// killough-style chained hash +// + +static void make_hash(void) +{ + int i; + unsigned char *rgb; + + for (i = PAL_SIZE, rgb = &palette_data[3*i]; rgb -= 3, --i >= 0; ) + { + memmove(hash[i].rgb, rgb, 3); + hash[i].next = hash[i].first = PAL_SIZE; + } + + for (i = PAL_SIZE, rgb = &palette_data[3*i]; rgb -= 3, --i >= 0; ) + { + int h = HASH(rgb) % PAL_SIZE; + hash[i].next = hash[h].first; + hash[h].first = i; + } +} + +// +// loadpal +// + +static void loadpal(const char *filename) +{ + void *data = NULL; + size_t size = read_or_die(&data, filename); + + if (size != 3*PAL_SIZE) + die("Bad palette: %s\n", filename); + + palette_data = data; +} + +// +// palette_init +// + +void palette_init(const char *filename) +{ + loadpal(filename); + make_hash(); +} + +// +// palette_getindex +// + +int palette_getindex(const unsigned char *rgb) +{ + int i; + + if (!palette_data) + die("No palette loaded - please specify one with -palette\n"); + + i = hash[HASH(rgb) % PAL_SIZE].first; + + while (i < PAL_SIZE && memcmp(hash[i].rgb, rgb, 3) != 0) + i = hash[i].next; + + return i < PAL_SIZE ? i : -1; +} diff --git a/data/rd_palette.h b/data/rd_palette.h new file mode 100644 index 0000000..db40ca9 --- /dev/null +++ b/data/rd_palette.h @@ -0,0 +1,10 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Chained hash lookup to convert rgb triples to palette indices + +// load raw palette, create hash table etc. +void palette_init(const char *filename); + +// lookup colour index of rgb triple +int palette_getindex(const unsigned char *rgb); diff --git a/data/rd_sound.c b/data/rd_sound.c new file mode 100644 index 0000000..c48efeb --- /dev/null +++ b/data/rd_sound.c @@ -0,0 +1,81 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Convert WAVE files to Doom sound format + +#include "config.h" + +#include +#include +#include + +#include "rd_util.h" +#include "rd_sound.h" + +// +// wav_to_doom +// +// loads a wav and converts it into Doom's sound format +// makes lots of assumptions about the format of a wav file. +// + +#ifdef _MSC_VER +#pragma pack(push) +#pragma pack(1) +#endif // _MSC_VER + +struct wav_header +{ + char riff[4]; + unsigned int size; + char wave[4]; + char fmt[4]; + unsigned int fmtlen; + unsigned short fmttag; + unsigned short channels; + unsigned int samplerate; + unsigned int byterate; + unsigned short blockalign; + unsigned short bits; + char data[4]; + unsigned int datalen; + char samples[1]; +} ATTR((packed)); + +struct doom_sound_header +{ + unsigned short log2bits; + unsigned short rate; + unsigned int length; + char samples[1]; +} ATTR((packed)); + +#ifdef _MSC_VER +#pragma pack(pop) +#endif // _MSC_VER + +size_t wav_to_doom(void **lumpdata, const char *filename) +{ + void *data; + size_t size = read_or_die(&data, filename); + struct wav_header *header = data; + struct doom_sound_header *out; + + if (size < sizeof(*header) - 1 + || memcmp(header->riff, "RIFF", 4) != 0 + || memcmp(header->wave, "WAVE", 4) != 0) + die("Invalid WAV file: %s\n", filename); + + size = sizeof(*out) - 1 + LONG(header->datalen); + out = xmalloc(size); + + out->log2bits = 3; + out->rate = SHORT(LONG(header->samplerate)); + out->length = header->datalen; + memmove(out->samples, header->samples, LONG(header->datalen)); + + free(data); + + *lumpdata = out; + return size; +} diff --git a/data/rd_sound.h b/data/rd_sound.h new file mode 100644 index 0000000..555d70f --- /dev/null +++ b/data/rd_sound.h @@ -0,0 +1,7 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Convert WAVE files to Doom sound format + +// convert wav file to doom sound format +size_t wav_to_doom(void **lumpdata, const char *filename); diff --git a/data/rd_util.c b/data/rd_util.c new file mode 100644 index 0000000..8176a27 --- /dev/null +++ b/data/rd_util.c @@ -0,0 +1,114 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Useful utility functions + +#include "config.h" + +#include +#include +#include +#include + +#include "rd_util.h" + +void ATTR((noreturn)) die(const char *error, ...) +{ + va_list args; + va_start(args, error); + vfprintf(stderr, error, args); + va_end(args); + + exit(1); +} + +void *xmalloc(size_t size) +{ + void *ptr = malloc(size); + + if (!ptr) + die("Failure to allocate %lu bytes\n", (unsigned long)size); + + return ptr; +} + +void *xrealloc(void *ptr, size_t size) +{ + ptr = realloc(ptr, size); + + if (!ptr) + die("Failure to reallocate %lu bytes\n", (unsigned long)size); + + return ptr; +} + +void *xcalloc(size_t n, size_t size) +{ + void *ptr = xmalloc(n * size); + memset(ptr, 0, n * size); + return ptr; +} + +char *xstrdup(const char *s) +{ + size_t size = strlen(s)+1; + void *ptr = xmalloc(size); + memcpy(ptr, s, size); + return ptr; +} + +static const char **search_paths = NULL; +static size_t num_search_paths = 0; + +// slurp an entire file into memory or kill yourself +size_t read_or_die(void **ptr, const char *file) +{ + size_t size = 0, length = 0; + void *buffer = NULL, *pos = buffer; + FILE *f; + + f = fopen(file, "rb"); + if (!f) + { + int i; + size_t s; + char *path; + + for (i = 0; i < num_search_paths; i++) + { + s = strlen(search_paths[i]) + 1 + strlen(file) + 1; + path = xmalloc(s); + snprintf(path, s, "%s/%s", search_paths[i], file); + f = fopen(path, "rb"); + free(path); + } + } + if (!f) + die("Cannot open %s\n", file); + + while (!feof(f)) + { + size_t size_read; + + if (size >= length) + { + size += 4096; + buffer = xrealloc(buffer, size); + } + + pos = (char *)buffer + length; // don't assume sizeof(void)==1 + size_read = fread(pos, 1, 4096, f); + length += size_read; + } + + *ptr = buffer; + fclose(f); + return length; +} + +void search_path(const char *path) +{ + search_paths = xrealloc(search_paths, + (++num_search_paths)*sizeof(*search_paths)); + search_paths[num_search_paths-1] = xstrdup(path); +} diff --git a/data/rd_util.h b/data/rd_util.h new file mode 100644 index 0000000..59a9cd0 --- /dev/null +++ b/data/rd_util.h @@ -0,0 +1,38 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Useful utility functions + +#ifdef __GNUC__ +#define ATTR(x) __attribute__(x) +#else +#define ATTR(x) +#endif + +#ifdef WORDS_BIGENDIAN +# ifdef __GNUC__ +#define LONG(x) __builtin_bswap32((x)) +#define SHORT(x) (__builtin_bswap32((x))>>16) +# else +#define LONG(x) ( (((x) & 0x000000FF) << 24) \ + +(((x) & 0x0000FF00) << 8) \ + +(((x) & 0x00FF0000) >> 8) \ + +(((x) & 0xFF000000) >> 24) ) +#define SHORT(x) ( (((x) & 0x00FF) << 8) \ + +(((x) & 0xFF00) >> 8) ) +# endif +#else +#define LONG(x) (x) +#define SHORT(x) (x) +#endif + +void ATTR((noreturn)) die(const char *error, ...); + +void *xmalloc(size_t size); +void *xrealloc(void *ptr, size_t size); +void *xcalloc(size_t n, size_t size); +char *xstrdup(const char *s); + +// slurp an entire file into memory or kill yourself +size_t read_or_die(void **ptr, const char *file); +void search_path(const char *path); diff --git a/data/sounds/dsdgact.wav b/data/sounds/dsdgact.wav new file mode 100644 index 0000000..3925dd5 Binary files /dev/null and b/data/sounds/dsdgact.wav differ diff --git a/data/sounds/dsdgatk.wav b/data/sounds/dsdgatk.wav new file mode 100644 index 0000000..3578877 Binary files /dev/null and b/data/sounds/dsdgatk.wav differ diff --git a/data/sounds/dsdgdth.wav b/data/sounds/dsdgdth.wav new file mode 100644 index 0000000..165456c Binary files /dev/null and b/data/sounds/dsdgdth.wav differ diff --git a/data/sounds/dsdgpain.wav b/data/sounds/dsdgpain.wav new file mode 100644 index 0000000..d4f8b07 Binary files /dev/null and b/data/sounds/dsdgpain.wav differ diff --git a/data/sounds/dsdgsit.wav b/data/sounds/dsdgsit.wav new file mode 100644 index 0000000..af8ace9 Binary files /dev/null and b/data/sounds/dsdgsit.wav differ diff --git a/data/sprites/dogsa1.ppm b/data/sprites/dogsa1.ppm new file mode 100644 index 0000000..b1c977e Binary files /dev/null and b/data/sprites/dogsa1.ppm differ diff --git a/data/sprites/dogsa2.ppm b/data/sprites/dogsa2.ppm new file mode 100644 index 0000000..1e1116d Binary files /dev/null and b/data/sprites/dogsa2.ppm differ diff --git a/data/sprites/dogsa3.ppm b/data/sprites/dogsa3.ppm new file mode 100644 index 0000000..0e83c45 Binary files /dev/null and b/data/sprites/dogsa3.ppm differ diff --git a/data/sprites/dogsa4.ppm b/data/sprites/dogsa4.ppm new file mode 100644 index 0000000..0d393c8 Binary files /dev/null and b/data/sprites/dogsa4.ppm differ diff --git a/data/sprites/dogsa5.ppm b/data/sprites/dogsa5.ppm new file mode 100644 index 0000000..bb6cb99 Binary files /dev/null and b/data/sprites/dogsa5.ppm differ diff --git a/data/sprites/dogsa6.ppm b/data/sprites/dogsa6.ppm new file mode 100644 index 0000000..c5e7095 Binary files /dev/null and b/data/sprites/dogsa6.ppm differ diff --git a/data/sprites/dogsa7.ppm b/data/sprites/dogsa7.ppm new file mode 100644 index 0000000..a8319e3 Binary files /dev/null and b/data/sprites/dogsa7.ppm differ diff --git a/data/sprites/dogsa8.ppm b/data/sprites/dogsa8.ppm new file mode 100644 index 0000000..ab6e33e Binary files /dev/null and b/data/sprites/dogsa8.ppm differ diff --git a/data/sprites/dogsb1.ppm b/data/sprites/dogsb1.ppm new file mode 100644 index 0000000..bd44841 Binary files /dev/null and b/data/sprites/dogsb1.ppm differ diff --git a/data/sprites/dogsb2.ppm b/data/sprites/dogsb2.ppm new file mode 100644 index 0000000..d43c28e Binary files /dev/null and b/data/sprites/dogsb2.ppm differ diff --git a/data/sprites/dogsb3.ppm b/data/sprites/dogsb3.ppm new file mode 100644 index 0000000..4bb5a51 Binary files /dev/null and b/data/sprites/dogsb3.ppm differ diff --git a/data/sprites/dogsb4.ppm b/data/sprites/dogsb4.ppm new file mode 100644 index 0000000..508a00d Binary files /dev/null and b/data/sprites/dogsb4.ppm differ diff --git a/data/sprites/dogsb5.ppm b/data/sprites/dogsb5.ppm new file mode 100644 index 0000000..0928309 Binary files /dev/null and b/data/sprites/dogsb5.ppm differ diff --git a/data/sprites/dogsb6.ppm b/data/sprites/dogsb6.ppm new file mode 100644 index 0000000..a225b4c Binary files /dev/null and b/data/sprites/dogsb6.ppm differ diff --git a/data/sprites/dogsb7.ppm b/data/sprites/dogsb7.ppm new file mode 100644 index 0000000..1c3e616 Binary files /dev/null and b/data/sprites/dogsb7.ppm differ diff --git a/data/sprites/dogsb8.ppm b/data/sprites/dogsb8.ppm new file mode 100644 index 0000000..2562e22 Binary files /dev/null and b/data/sprites/dogsb8.ppm differ diff --git a/data/sprites/dogsc1.ppm b/data/sprites/dogsc1.ppm new file mode 100644 index 0000000..3c04b2a Binary files /dev/null and b/data/sprites/dogsc1.ppm differ diff --git a/data/sprites/dogsc2.ppm b/data/sprites/dogsc2.ppm new file mode 100644 index 0000000..71f3b7a Binary files /dev/null and b/data/sprites/dogsc2.ppm differ diff --git a/data/sprites/dogsc3.ppm b/data/sprites/dogsc3.ppm new file mode 100644 index 0000000..6f3f77f Binary files /dev/null and b/data/sprites/dogsc3.ppm differ diff --git a/data/sprites/dogsc4.ppm b/data/sprites/dogsc4.ppm new file mode 100644 index 0000000..b38085c Binary files /dev/null and b/data/sprites/dogsc4.ppm differ diff --git a/data/sprites/dogsc5.ppm b/data/sprites/dogsc5.ppm new file mode 100644 index 0000000..981da52 Binary files /dev/null and b/data/sprites/dogsc5.ppm differ diff --git a/data/sprites/dogsc6.ppm b/data/sprites/dogsc6.ppm new file mode 100644 index 0000000..1f1fe57 Binary files /dev/null and b/data/sprites/dogsc6.ppm differ diff --git a/data/sprites/dogsc7.ppm b/data/sprites/dogsc7.ppm new file mode 100644 index 0000000..b93ed09 Binary files /dev/null and b/data/sprites/dogsc7.ppm differ diff --git a/data/sprites/dogsc8.ppm b/data/sprites/dogsc8.ppm new file mode 100644 index 0000000..470e03c Binary files /dev/null and b/data/sprites/dogsc8.ppm differ diff --git a/data/sprites/dogsd1.ppm b/data/sprites/dogsd1.ppm new file mode 100644 index 0000000..45f26b5 Binary files /dev/null and b/data/sprites/dogsd1.ppm differ diff --git a/data/sprites/dogsd2.ppm b/data/sprites/dogsd2.ppm new file mode 100644 index 0000000..7fdd3ea Binary files /dev/null and b/data/sprites/dogsd2.ppm differ diff --git a/data/sprites/dogsd3.ppm b/data/sprites/dogsd3.ppm new file mode 100644 index 0000000..80b7e4c Binary files /dev/null and b/data/sprites/dogsd3.ppm differ diff --git a/data/sprites/dogsd4.ppm b/data/sprites/dogsd4.ppm new file mode 100644 index 0000000..32b30bf Binary files /dev/null and b/data/sprites/dogsd4.ppm differ diff --git a/data/sprites/dogsd5.ppm b/data/sprites/dogsd5.ppm new file mode 100644 index 0000000..fe82135 Binary files /dev/null and b/data/sprites/dogsd5.ppm differ diff --git a/data/sprites/dogsd6.ppm b/data/sprites/dogsd6.ppm new file mode 100644 index 0000000..19f372e Binary files /dev/null and b/data/sprites/dogsd6.ppm differ diff --git a/data/sprites/dogsd7.ppm b/data/sprites/dogsd7.ppm new file mode 100644 index 0000000..9e8bd22 Binary files /dev/null and b/data/sprites/dogsd7.ppm differ diff --git a/data/sprites/dogsd8.ppm b/data/sprites/dogsd8.ppm new file mode 100644 index 0000000..819a823 Binary files /dev/null and b/data/sprites/dogsd8.ppm differ diff --git a/data/sprites/dogse1.ppm b/data/sprites/dogse1.ppm new file mode 100644 index 0000000..493d272 Binary files /dev/null and b/data/sprites/dogse1.ppm differ diff --git a/data/sprites/dogse2.ppm b/data/sprites/dogse2.ppm new file mode 100644 index 0000000..8a3b6f9 Binary files /dev/null and b/data/sprites/dogse2.ppm differ diff --git a/data/sprites/dogse3.ppm b/data/sprites/dogse3.ppm new file mode 100644 index 0000000..5f4a024 Binary files /dev/null and b/data/sprites/dogse3.ppm differ diff --git a/data/sprites/dogse4.ppm b/data/sprites/dogse4.ppm new file mode 100644 index 0000000..baf433b Binary files /dev/null and b/data/sprites/dogse4.ppm differ diff --git a/data/sprites/dogse5.ppm b/data/sprites/dogse5.ppm new file mode 100644 index 0000000..874ca2a Binary files /dev/null and b/data/sprites/dogse5.ppm differ diff --git a/data/sprites/dogse6.ppm b/data/sprites/dogse6.ppm new file mode 100644 index 0000000..6ed2319 Binary files /dev/null and b/data/sprites/dogse6.ppm differ diff --git a/data/sprites/dogse7.ppm b/data/sprites/dogse7.ppm new file mode 100644 index 0000000..123af5c Binary files /dev/null and b/data/sprites/dogse7.ppm differ diff --git a/data/sprites/dogse8.ppm b/data/sprites/dogse8.ppm new file mode 100644 index 0000000..36a599e Binary files /dev/null and b/data/sprites/dogse8.ppm differ diff --git a/data/sprites/dogsf1.ppm b/data/sprites/dogsf1.ppm new file mode 100644 index 0000000..3392b21 Binary files /dev/null and b/data/sprites/dogsf1.ppm differ diff --git a/data/sprites/dogsf2.ppm b/data/sprites/dogsf2.ppm new file mode 100644 index 0000000..3047eda Binary files /dev/null and b/data/sprites/dogsf2.ppm differ diff --git a/data/sprites/dogsf3.ppm b/data/sprites/dogsf3.ppm new file mode 100644 index 0000000..b9f7099 Binary files /dev/null and b/data/sprites/dogsf3.ppm differ diff --git a/data/sprites/dogsf4.ppm b/data/sprites/dogsf4.ppm new file mode 100644 index 0000000..ba49039 Binary files /dev/null and b/data/sprites/dogsf4.ppm differ diff --git a/data/sprites/dogsf5.ppm b/data/sprites/dogsf5.ppm new file mode 100644 index 0000000..9e94026 Binary files /dev/null and b/data/sprites/dogsf5.ppm differ diff --git a/data/sprites/dogsf6.ppm b/data/sprites/dogsf6.ppm new file mode 100644 index 0000000..8602d1a Binary files /dev/null and b/data/sprites/dogsf6.ppm differ diff --git a/data/sprites/dogsf7.ppm b/data/sprites/dogsf7.ppm new file mode 100644 index 0000000..1678618 Binary files /dev/null and b/data/sprites/dogsf7.ppm differ diff --git a/data/sprites/dogsf8.ppm b/data/sprites/dogsf8.ppm new file mode 100644 index 0000000..cb0deed Binary files /dev/null and b/data/sprites/dogsf8.ppm differ diff --git a/data/sprites/dogsg1.ppm b/data/sprites/dogsg1.ppm new file mode 100644 index 0000000..758199c Binary files /dev/null and b/data/sprites/dogsg1.ppm differ diff --git a/data/sprites/dogsg2.ppm b/data/sprites/dogsg2.ppm new file mode 100644 index 0000000..2ff2233 Binary files /dev/null and b/data/sprites/dogsg2.ppm differ diff --git a/data/sprites/dogsg3.ppm b/data/sprites/dogsg3.ppm new file mode 100644 index 0000000..4fb9b1f Binary files /dev/null and b/data/sprites/dogsg3.ppm differ diff --git a/data/sprites/dogsg4.ppm b/data/sprites/dogsg4.ppm new file mode 100644 index 0000000..b1d0c27 Binary files /dev/null and b/data/sprites/dogsg4.ppm differ diff --git a/data/sprites/dogsg5.ppm b/data/sprites/dogsg5.ppm new file mode 100644 index 0000000..2e4e5f6 Binary files /dev/null and b/data/sprites/dogsg5.ppm differ diff --git a/data/sprites/dogsg6.ppm b/data/sprites/dogsg6.ppm new file mode 100644 index 0000000..730ff99 Binary files /dev/null and b/data/sprites/dogsg6.ppm differ diff --git a/data/sprites/dogsg7.ppm b/data/sprites/dogsg7.ppm new file mode 100644 index 0000000..4206e97 Binary files /dev/null and b/data/sprites/dogsg7.ppm differ diff --git a/data/sprites/dogsg8.ppm b/data/sprites/dogsg8.ppm new file mode 100644 index 0000000..a8ccc1a Binary files /dev/null and b/data/sprites/dogsg8.ppm differ diff --git a/data/sprites/dogsh1.ppm b/data/sprites/dogsh1.ppm new file mode 100644 index 0000000..48ae068 Binary files /dev/null and b/data/sprites/dogsh1.ppm differ diff --git a/data/sprites/dogsh2.ppm b/data/sprites/dogsh2.ppm new file mode 100644 index 0000000..313eb3c Binary files /dev/null and b/data/sprites/dogsh2.ppm differ diff --git a/data/sprites/dogsh3.ppm b/data/sprites/dogsh3.ppm new file mode 100644 index 0000000..03c30c4 Binary files /dev/null and b/data/sprites/dogsh3.ppm differ diff --git a/data/sprites/dogsh4.ppm b/data/sprites/dogsh4.ppm new file mode 100644 index 0000000..d2d113d Binary files /dev/null and b/data/sprites/dogsh4.ppm differ diff --git a/data/sprites/dogsh5.ppm b/data/sprites/dogsh5.ppm new file mode 100644 index 0000000..8d06946 Binary files /dev/null and b/data/sprites/dogsh5.ppm differ diff --git a/data/sprites/dogsh6.ppm b/data/sprites/dogsh6.ppm new file mode 100644 index 0000000..e07aef8 Binary files /dev/null and b/data/sprites/dogsh6.ppm differ diff --git a/data/sprites/dogsh7.ppm b/data/sprites/dogsh7.ppm new file mode 100644 index 0000000..57cd436 Binary files /dev/null and b/data/sprites/dogsh7.ppm differ diff --git a/data/sprites/dogsh8.ppm b/data/sprites/dogsh8.ppm new file mode 100644 index 0000000..176f0a7 Binary files /dev/null and b/data/sprites/dogsh8.ppm differ diff --git a/data/sprites/dogsi0.ppm b/data/sprites/dogsi0.ppm new file mode 100644 index 0000000..a8a93fc Binary files /dev/null and b/data/sprites/dogsi0.ppm differ diff --git a/data/sprites/dogsj0.ppm b/data/sprites/dogsj0.ppm new file mode 100644 index 0000000..1f75eb9 Binary files /dev/null and b/data/sprites/dogsj0.ppm differ diff --git a/data/sprites/dogsk0.ppm b/data/sprites/dogsk0.ppm new file mode 100644 index 0000000..e2ea8b7 Binary files /dev/null and b/data/sprites/dogsk0.ppm differ diff --git a/data/sprites/dogsl0.ppm b/data/sprites/dogsl0.ppm new file mode 100644 index 0000000..8274ac4 Binary files /dev/null and b/data/sprites/dogsl0.ppm differ diff --git a/data/sprites/dogsm0.ppm b/data/sprites/dogsm0.ppm new file mode 100644 index 0000000..37c054c Binary files /dev/null and b/data/sprites/dogsm0.ppm differ diff --git a/data/sprites/dogsn0.ppm b/data/sprites/dogsn0.ppm new file mode 100644 index 0000000..4a8aefa Binary files /dev/null and b/data/sprites/dogsn0.ppm differ diff --git a/data/sprites/pls1a0.ppm b/data/sprites/pls1a0.ppm new file mode 100644 index 0000000..dadf751 Binary files /dev/null and b/data/sprites/pls1a0.ppm differ diff --git a/data/sprites/pls1b0.ppm b/data/sprites/pls1b0.ppm new file mode 100644 index 0000000..0e4f877 Binary files /dev/null and b/data/sprites/pls1b0.ppm differ diff --git a/data/sprites/pls1c0.ppm b/data/sprites/pls1c0.ppm new file mode 100644 index 0000000..dadf751 Binary files /dev/null and b/data/sprites/pls1c0.ppm differ diff --git a/data/sprites/pls1d0.ppm b/data/sprites/pls1d0.ppm new file mode 100644 index 0000000..0e4f877 Binary files /dev/null and b/data/sprites/pls1d0.ppm differ diff --git a/data/sprites/pls1e0.ppm b/data/sprites/pls1e0.ppm new file mode 100644 index 0000000..61d40cf Binary files /dev/null and b/data/sprites/pls1e0.ppm differ diff --git a/data/sprites/pls1f0.ppm b/data/sprites/pls1f0.ppm new file mode 100644 index 0000000..61d40cf Binary files /dev/null and b/data/sprites/pls1f0.ppm differ diff --git a/data/sprites/pls1g0.ppm b/data/sprites/pls1g0.ppm new file mode 100644 index 0000000..64bb4b2 Binary files /dev/null and b/data/sprites/pls1g0.ppm differ diff --git a/data/sprites/pls2a0.ppm b/data/sprites/pls2a0.ppm new file mode 100644 index 0000000..9dee589 Binary files /dev/null and b/data/sprites/pls2a0.ppm differ diff --git a/data/sprites/pls2b0.ppm b/data/sprites/pls2b0.ppm new file mode 100644 index 0000000..7419e60 Binary files /dev/null and b/data/sprites/pls2b0.ppm differ diff --git a/data/sprites/pls2c0.ppm b/data/sprites/pls2c0.ppm new file mode 100644 index 0000000..c8850a9 Binary files /dev/null and b/data/sprites/pls2c0.ppm differ diff --git a/data/sprites/pls2d0.ppm b/data/sprites/pls2d0.ppm new file mode 100644 index 0000000..579cdf6 Binary files /dev/null and b/data/sprites/pls2d0.ppm differ diff --git a/data/sprites/pls2e0.ppm b/data/sprites/pls2e0.ppm new file mode 100644 index 0000000..58b2017 Binary files /dev/null and b/data/sprites/pls2e0.ppm differ diff --git a/data/sprites/tnt1a0.ppm b/data/sprites/tnt1a0.ppm new file mode 100644 index 0000000..2a25218 Binary files /dev/null and b/data/sprites/tnt1a0.ppm differ diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 0000000..a1acd53 --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,27 @@ +set(DOC_FILES + ../NEWS + boom.txt + DeePBSPV4specs.txt + MBFFAQ.txt + MBF.txt + prboom-plus-usage.txt + README.command-line + README.compat + README.demos + umapinfo.txt +) +set(MAN5_FILES + prboom-plus.cfg.5 +) +set(MAN6_FILES + prboom-plus.6 + prboom-plus-game-server.6 +) + +if(${CMAKE_VERSION} VERSION_LESS "3.14.0") + install(FILES ${DOC_FILES} DESTINATION "${CMAKE_INSTALL_DOCDIR}" COMPONENT "Documentation") +else() + install(FILES ${DOC_FILES} TYPE DOC COMPONENT "Documentation") +endif() +install(FILES ${MAN5_FILES} DESTINATION "${CMAKE_INSTALL_MANDIR}/man5" COMPONENT "Manpages") +install(FILES ${MAN6_FILES} DESTINATION "${CMAKE_INSTALL_MANDIR}/man6" COMPONENT "Manpages") diff --git a/doc/DeePBSPV4specs.txt b/doc/DeePBSPV4specs.txt new file mode 100644 index 0000000..966a661 --- /dev/null +++ b/doc/DeePBSPV4specs.txt @@ -0,0 +1,101 @@ + +Description of DeePBSP v4 Extended nodes for developers +------------------------------------------------------- + +This information is intended for any port author that wishes to extend the size +of level the port can play. It's suggested to change the standard level structures +to also have unsigned value to support large levels. For example, change the sidedef +reference to unsigned short for the linedef struct. Then use 0xFFFF (#define this) to +check for no sidedef and store -1 in an internal format as int for the sidedef. + +Other versions can easily be made if somebody has a reason for a change that +reflects the real world. + +CAUTION : Be sure to turn of struct alignment in the compiler, otherwise + it may add data to the structs. + +Terminology is per Intel processors. + + --- V4 Standard nodes only --- + +struct Segment4 +{ + int vstart; // from this vertex ... (allows for over 64k verts which + int vend; // ... to this vertex (can occur during node building with packed sidedefs + unsigned short angle; // angle (0 = east, 16384 = north, ...) + unsigned short linedef; // linedef that this seg goes along (allows for 64k linedefs) + short flip; // true if not the same direction as linedef + unsigned short dist; // distance from starting point +}; + +-- BOTH standard V4 nodes and V4 GLNodes -- + +struct Pnode4 +{ + short X, Y; // x,y coordinates + short dX, dY; // offsets + short bbox[2][4]; // bounding rectangles + int chright, chleft; // Node or SubSector (if bit 31 is set) +}; + + +struct SubSector4 +{ + unsigned short segcount; // number of Segs in this SubSector. 64k is plenty, hence no int + int firstseg; // first Seg +}; + + --- GL nodes only--- + +struct GLSeg4 +{ + int vstart; // bit 31 set= GL vert + int vend; // ... + unsigned short linedef; // linedef that this seg goes along (allows for for 64k linedefs) + unsigned short side; // 0 if on right of linedef, 1 if on left +} + + +--- detection the version for V4 STANDARD nodes + +The NODES lump at offset 0 contains "xNd4\0\0\0\0" if V4 nodes are stored. The 0x000000 is pretty +much an impossible value in the stock node structure, so this is very reliable. + +Use the above structures to decode and map the information into the ports internal format. + +Please note that the information is identical to the stock node format with the +changes only in sign and size. + +To use extended V4 nodes, the internal structure of a port should have int values for +linedefs, sidedefs and vertices. Afer that change it becomes pretty easy to support both +the old and new formats as well as levels with more than 32k sidedefs or vertices. + +The only major change is for sidedefs. When a level is read, check the linedef sidedef +reference for 0xFFFF and then store a -1 in the internal format (depends on compiler used). + +Other internal formats need to be made either unsigned short (vs short) or made int. + +--- detecting the version for V4 GLNODES + +Vertexs are the same format as GL version 2, however, the "magic" is different so you can tell +the format of the other lumps. There is no need to store a "magic" number in the other lumps +and this also prevents GL2 only ports from misinterpreting. + +Thus if the Nodes are V4 then at location 0 is "gNd4" vs "gNd2" in the the VERTEX lump. This +avoids the accidental confusion that can occur with V3 GL nodes that still has "gNd2" even +though the other parts are different. + +The same comments apply as for STANDARD nodes. Some ports have all the right stuff, it's just +a matter of changing a few level structures from signed to unsigned and check for 0xFFFF as a +sidedef value and then storing -1 in the internal structure. + +The old v2 "partner" value in segs is not stored in v4 since neither JDOOM, PRBOOM or VAVOOM used this +value. Same thing goes for linedef references. I think 64k is plenty considering that the sidedefs +really hit the limit way before. + +The only way to get more than 64k sidedefs is to pack the sidedefs, but then the +level becomes basically uneditable. A level like that can't be saved as is without packing it +again. + +Vertex limit of 64k is also plenty. The GLnodes only need int so they can distinquish the vertex type. + diff --git a/doc/MBF.txt b/doc/MBF.txt new file mode 100644 index 0000000..db567bd --- /dev/null +++ b/doc/MBF.txt @@ -0,0 +1,366 @@ +Marine's Best Friend + +------------------------------------------------------------------------------- + +Programmer: Lee Killough + +Artist: Len Pitre + +PlayTesters: Ky (Rez) Moffet, Len Pitre, James (Quasar) Haley + +Canine Consulting: Longplain Kennels, Reg'd + +Sound Code: Shawn Hargreaves & Allegro Team + +Additional Credit: id Software, TeamTNT + +Special Thanks To: John Romero, Joel Murdoch + +------------------------------------------------------------------------------- + +Marine's Best Friend is a modification of the Doom source. + +Features + +* Friendly Monster AI + + Friends attack enemies like players + + Friendliness is a mobj property, like NOGRAVITY or SOLID + + Friendly single-player helpers (dogs) can fill in coop starts + + Friendliness is transferred by Arch-Vile, Pain Elemental, and Boss Spawner + + Player autoaiming avoids friends if enemies are in range + + Friends with missiles return one shot if shot by friends + + Friends stay a "comfortable distance" away from players + + Friends indicated by different color in automap + +* AI Improvements + + Avoid hazards (e.g. crushing ceilings) + + Stop what they're doing and rescue dying friends + + Follow enemies up lifts, instead of wandering off randomly + + Back away from nearby enemies if advantageous + + Avoid shooting friends who get in their way + + Infighting when provoked by own kind (i.e. Doom's behavior) made optional + + (Development) Code and data restructured to allow more efficient AI + +* Doom Pre-Beta ("Press Release") Version Emulation + + Classic BFG, Plasma Rifle, Chaingun, Rocket Launcher + + Classic BFG allowed in regular (non-emulation) mode + + Original Flaming Skulls + + The 3 demo levels, "remastered" + + Nightvision goggles, skullchests, demonic daggers, unholy bibles, etc. + + Other Press Release version behaviors (e.g. invisibility, invulnerability) + +* Improved Video Options + + + 640x400 high-detail mode, without bad side effects: + - Doom world scenes and automap are rendered in 640x400 for higher detail + - Doom's aspect ratio is preserved exactly (no elongated textures/sprites) + - Status bar, menus, text, intermission screens all remain full-sized + - Optional hardware page-flipping + + + Wait-free page-flipped 320x200 ModeX for smoother play on slow machines + + + Doom flashing disk icon reimplemented (was removed in linux port) + + + Video options can be changed in realtime in General menu + - Hires + - Page-flipping + - Wait for vertical retrace + - Translucency enable + - Translucency percentage + - PCX vs. BMP screenshots + - Doom flashing disk icon + - HOM cheat code optionally flashing or solid red + +* DEH/BEX files embedded inside of wads (DEHACKED lump) + +* OPTIONS lump for overriding config settings inside wads + + Allows wad authors to set game options inside wads + + Options are still editable by player, and are remembered on a per-wad basis + + Doesn't interfere with the player's own defaults when not playing the wad + +* Menu option to load up to two WAD and two DEH/BEX files at program startup + + -noload command-line override switch + +* Improved Menus + + + Informative warnings printed on menu screens when: + - changed option doesn't take effect until next new game or idclev + - changed option doesn't take effect until entire program is restarted + - option's current setting (e.g. loadgame) differs from its default setting + + + New "General" option menu, allows changing of many settings inside game + - Video + - Sound & Music + - Input Devices + - .WAD and .DEH/.BEX Files Preloaded at Startup + - Miscellaneous + + + New "Doom Compatibility" setup menu allows fine-control of all Doom + compatibility settings, instead of having only one "compatibility" + variable. + + + Numeric input improved + - Negative values are allowed + - Setup does not change game variables until input is "committed" + - Illegal or aborted input leaves game variables unchanged + - Backspace allowed during input + + + SSG weapon preferences allowed to be edited while playing Ultimate Doom + + + New hotkey for entering Setup Menu in one stroke during game + + + (Development) Filenames allowed to be entered + + + (Development) Menu items can span multiple lines on the screen + + + (Development) Action functions can be called after items are edited, + such as to change video mode in real-time + +* Shotgun and Fist may be selected without toggling with the SSG/Chainsaw + +* .lmp extension optional with -file if .wad file doesn't also exist + +* Improved Automap + + Option to display pointer coordinates instead of player coordinates + + Automap setup menu rearranged for easier use + +* Improved Demos + + Pause allowed during demo playback + + Single-player games can be saved during demo playback, to take snapshots + + -recordfrom option added back, allowing demos to start from a saved game + + Demos don't desync just because menu is accessed during recording + + During demo playback, automap and menu are easier to use + + No switching out of automap when player dies during demo playback + + (Development) demo_version global variable to help preserve demo sync + +* Improved Movement Clipping + + Players can get out of 1s or 2s walls when they are otherwise stuck + + Monsters do not get stuck hanging partially off of tall ledges + + Monsters can move up and down stairs more easily (compatibility-optioned) + +* More Realism (compatibility-optioned) + + Non-sentient objects fall down under their own weight when their balance + is upset and they are more than halfway off of a ledge + + Objects falling off tall ledges follow parabolic trajectory + + Live monsters may be pushed off ledges, and intelligently try to recover + +* New Bouncy Things (mobj) Flag + + Missiles bounce off of floors and ceilings, such as classic BFG fireballs + + Floating "bouncy" monsters bounce up and down if under gravity + + Solid objects bounce off of walls and roll up and down stairs + + DEH frames support bouncing grenades/pipebombs + +* Touchy Things (mobj) Flag + + Mines which detonate when touched or disturbed + + Monsters which die when touched + +* Doom Bugs Fixed (compatibility-optioned, where appropriate) + + Fireballs (especially Mancubus) going through walls + + Monsters stuck at doortracks or hanging over tall ledges + + Slime trails reduced (caused by WAD coordinate system's limited precision) + + Zombie players exiting levels, forever remaining zombies + + Sky is unaffected by invulnerability colormap + + On MAP30, any monster can telefrag anyone else + + Boss spawners can't telefrag on any levels except MAP30 + + Soundtargets not remembered across savegames + + Flickering light not remembered across savegames + + Fractional floor & ceiling heights and xy offsets not saved in savegames + + Revenant Tracers' tracking state not preserved across savegames + + Raise to shortest texture considers "-" == "AASHITTY" + + SSG shows reload frame while firing frame is still displayed + + SSG neck is not lit up correctly when SSG is fired in a dark room + + Demo desync if menu is accessed in the middle of recording a demo + + Illegal menu activities during demos (e.g. loadgame during recording) + + Arch-Vile hellfire spawned in wrong position + + Voodoo dolls push up against wall as long as the real player is moving + + Deleted thinkers referenced by monster targets/tracers, or soundtargets + + Top of pistol appears at the bottom of screen when game starts + + Monsters sometimes fire missiles back at wrong player when hit painfully + + Extra-hard player death sound not working in Registered / Ultimate Doom + +* Boom Bugs Fixed (Note: these are not TeamTNT fixes) + + + Doom incompatibilities fixed, with new option switches + - Stairbuilding + - Cheat code infinite duration + - God mode cheat is absolute + - Linedef effects with tag 0, e.g. all-lights-off effects + + + Bobbing / friction + - Bobbing based only on player-applied thrust, instead of total speed + - Friction algorithm made more efficient, and made sector-based + - Fixed bug which stopped all momentum when up against wall + - Friction (and wall-bouncing on ice) work on all objects, not just players + - Bobbing does not jerk when player moves in and out of icy sectors (v2.02) + - Sludge slowdown fixed + - Wall-running restored to Doom efficiency + + + Wind affects all sentient / shootable objects + + + Player corpse queue =traversal order= (caused weapon pickup problems in DM) + + + Fix opening limit removal + + + Response files + - Optional .rsp extension + - Correct error-handling (page faulted before) + + + Fix underwater slowdown + + + Fix sudden light changes over water sectors + + + Fix things disappearing over water sectors + + + Fix underwater sprite lighting (Two bugs: One in v2.01, another in v2.02) + + + Menu reset-to-defaults code made more robust, to prevent "false resets" + + + Monsters-stuck-on-doortracks have less turning (v2.02 bug) + + + Fixed message review w/ background messing up in smaller screen size + + + Aliasing of wall scrolling speeds > a certain speed, due to silent overflow + + + Fix new map thing flags' causing incompatibility with certain Doom wads + + + Fix filehandle leak in translucency code + + + Default skill level not remembered correctly across multiple games + + + Netgame inconsistencies if options are chosen differently (v2.02) + + + Referencing freed data pointed to by monsters' lastenemy field + + + Tagged DR doors' lighting effects made gradual, instead of totally on/off + + + Menu confirmation messages (yes/no) no longer limited to 40 characters + (Garbage at end of QuickLoad message fixed) + + + Sounds heard randomly in wrong locations (e.g. weapons fire, player death) + + + HUD counter update problem + + + Coop mode slowdown, a.k.a. Monster Medusa + + + Inconsistent use of stdout/stderr in output messages + + + SegViol if key_enter held down during program startup (message review) + + + Verbose "missing patch" error messages not printed unless -devparm used + + + Setup menu backspace + + + Indigo/Brown default chat keys reversed + + + Final Doom support + + + DEH + - Writing to dehout.txt replaced with -dehout option + - Fixed thing flag mnemonics bugs (TRANSLATION1/2, error detection) + - Hex allowed in numeric input (e.g. 0x1234abcd) + - Blank separation-line handling fixed + - Fixed memory leak + - Several SegViols fixed + - -bex may be used as a synonym of -deh + + + Screenshot, .cfg file, and savegame error-handling improved + - User-friendly messages printed when error occurs (e.g. "Disk Full") + - Original .cfg file left intact if new one cannot be written safely + + + Music + - Random noise heard in music (e.g. MM2 MAP23) + - MUS2MID programming errors (array size too small & undefined C behavior) + - Overall music quality improved + +* WAD error autocorrection, to prevent game crashes + + Clear 2s flag in one-sided linedefs + + Fill in missing right sidedefs with dummy sidedef + +* Improved Messages + + Message review list may scroll up or down + + Message review list may be temporary or permanent when activated + + Message review list and normal Doom messages toggled more intuitively + + Message timers are configurable (normal, review, chat) + +* Blockmap + + Blockmap generation algorithm rewritten (Note: this is not TeamTNT's) + + Improved dynamic blockmap list maintenance (more robust and faster) + +* Multiple -file and -deh options may be used on command-line / response file + +* User-comments allowed to be embedded inside of .cfg files, and are preserved + +* Performance Tuning + + Assembly blit routines tuned for machines above and below P6-class + + Monster target searches made faster by way of a multithreaded target list + + Fixed-point arithmetic made faster + + Sprite edge sorting made faster + + Tuned register allocation and other compiler optimization flags + + Sound FX code rewritten to be more efficient (in both memory and speed) + + Gameplay more responsive + +* Mobj pointer reference-counting added, to fix bugs and make game faster + +* New code pointers + + Variable-damage explosion + + Mushroom cloud explosion + + Linedef activation via code pointers + + Object spawning code pointer + +* New sky property-transfer linedef types (271-272) + + Arbitrary opaque wall textures can be transferred to skies + + Unlimited number of sky textures per level + + No external info lumps necessary, no "per-level" programming required + + Textures optionally flipped horizontally (as in Doom) + + Long-period rotating skies (e.g. moon's orbit, starfield) + + Animated skies + +* Removed all references to TeamTNT/Boom (except in source comments and docs) + + + TNT cheat code prefixes removed + - TNTEM replaced with KILLEM + - TNTKA replaced with IDK, without introducing .deh incompatibilities + + + "Boom" filename strings replaced (boomsav[0-7].dsg, boom.cfg, etc.) + + + ENDBOOM lump replaced with ENDOOM, restoring Doom behavior on end screens + +------------------------------------------------------------------------------- + +MBF Files + + File Description + + mbf.exe MBF executable +betagrph.wad Graphics for beta emulation +betalevl.wad The 3 Demo levels for beta emulation + mbfedit.txt Describes the WAD/DEH editing features new to MBF + options.txt Describes the command-line/setup menu options new to MBF + mbffaq.txt FAQ about MBF + mbf.txt This file + asetup.exe Allegro setup program + snddrvr.txt Notes concerning sound drivers (from Boom distribution) + doom17.dat DCK2.2-f configuration file for editing MBF wads + common.cfg DETH configuration file for editing MBF wads + cwsdpmi.exe CWS DPMI host +examples.zip Small demonstrations of MBF features + copying.dj DJ Delorie Copyleft stuff + copying GPL license stuff + +------------------------------------------------------------------------------- + +DISCLAIMERS + +MBF is unsupported software. There are no warranties as to its fitness for any +particular purpose. There are also no plans for future updates. + +MBF is a single-player game. Multiplayer games may or may not work (some people +say they do work). Support files for multiplayer games (e.g. sersetup.exe) are +not included, but might be available elsewhere. + +MBF is based on the Boom v2.02 source, but it is not supported by TeamTNT or +id Software. Don't ask them about MBF. + +------------------------------------------------------------------------------- +Lee Killough Last Updated 12/22/1998 + diff --git a/doc/MBFFAQ.txt b/doc/MBFFAQ.txt new file mode 100644 index 0000000..cdbdf79 --- /dev/null +++ b/doc/MBFFAQ.txt @@ -0,0 +1,148 @@ +Marine's Best Friend +Frequently Asked Questions + +------------------------------------------------------------------------------- + +Q: Why don't the friendly dogs retaliate when I shoot them? + +A1. The dog is a close-attack monster (no missiles), and therefore to attack, + it would need to stop whatever it was doing and return to the player, and + it might not be able to reach the player before being attacked again by + another enemy, which is its prime concern. Even if it could reach the + player, it would only bite the player once, since friendly monsters don't + turn around and become enemies simply because they are hit once -- they + instead use tit-for-tat, returning friendly fire once, for each time they + are hit. + +A2. This is normal behavior for real dogs, according to one dog expert. + +------------------------------------------------------------------------------- + +Q: Why do dogs readily go up steep or narrow stairs, but then sometimes won't + come back down? + +A: According to a canine behavioural expert, this is consistent with the + typical behaviour of real dogs. + +------------------------------------------------------------------------------- + +Q: Why don't I see any new levels with -beta? + +A: You must add -file betalevl to your command-line, to load the levels. The + levels in betalevl.wad are E1M2, E2M5, and E3M2, exactly as in the Press + Release Version of Doom. If you only use -beta, you will be enabling the + beta's features, but without the levels. + +------------------------------------------------------------------------------- + +Q: I get errors when I try to load betalevl.wad + +A: betalevl.wad requires Registered Doom I (or Ultimate Doom) as the iwad. + However, the -beta command-line option, which enables the graphics and + game changes, does not require Doom I to be used, and it can emulate beta + features under Doom II. + +------------------------------------------------------------------------------- + +Q: When using -beta, why is there no teleporter fog? Or, why does player + invisibility look like invulnerability? Why are there no palette changes + when the radsuit is used, and why doesn't it wear off with time? + +A: These behaviors are all consistent with the Press Release version of Doom, + alternatively called the "beta". See: + + ftp://ftp.cdrom.com/pub/doom/historic/ + +------------------------------------------------------------------------------- + +Q: Why is -beta a command-line option, and why must demos recorded with -beta + be played back with -beta? + +A: The betagrph.wad file must be loaded, and several internal data structures + must be modified, prior to game startup. Allowing -beta to be turned on and + off during the game would introduce a lot of complications. + +------------------------------------------------------------------------------- + +Q: Why is there a separate option for the Classic BFG, but not for all of the + other -beta features? + +A1. Not enough popular demand, given the complexity. Making individual beta + features optionable would require storing each one's selection status in + demos, savegames, etc., and it would require work to add them to the menus. + Probably only the Classic BFG is in enough demand to make a separate option + for it. + +A2. The features such as the beta plasma rifle and the beta lost souls, or + the green nightvision goggles, require a lot of wad data to be loaded, + which -beta does by loading the betagrph.wad file. The Classic BFG only + requires two extra fireball sprites, which do not take a lot of memory, + and thus can be unconditionally tucked away inside the .exe. + +------------------------------------------------------------------------------- + +Q: But doesn't the plasma rifle simply need to shoot the alternating fireballs + instead of blue plasma, making it no more difficult than the BFG? + +A: Actually, no. The plasma rifle sprites in Doom have some blue plasma + superimposed around the mouth of the plasma rifle. To remove this blue + plasma, new weapon sprites are needed. New weapon sprites are NOT needed + for the BFG, however. + +------------------------------------------------------------------------------- + +Q: Why can't the Unholy Cathedral beta level (E2M5 in betalevl.wad) be + finished, and why are some areas inaccessible? + +A: This is consistent with the original Press Release version's levels. + Wad errors were intentionally not fixed. + +------------------------------------------------------------------------------- + +Q: But there are some slime trails and HOM in the original levels, which don't + show up in betalevl.wad!!! + +A: Blatant rendering errors like HOM and slime trails were fixed. Even if they + weren't fixed, the slime trails would be hard to reproduce exactly because + they are inherently nondeterministic. + + The criteria used when making changes were: + + Rendering errors (e.g. HOM, slime trails): Fixed. + + Aesthetic errors (e.g. wall texture choices, texture alignment): Not Fixed. + + Level design errors (e.g. inaccessible areas of map): Not Fixed. + +------------------------------------------------------------------------------- + +Q: Why does the beta chaingun lower when I fire it in fullscreen mode? + +A: This is to workaround a problem with the Press Release version's chaingun + sprites. The sprites were pre-clipped with respect to the status bar, and + the status bar was always turned on in the Press Release version. With + the status bar turned on, the -beta chaingun looks like the original. In + fullscreen mode, however, it must be lowered while it is being fired, or + else parts of the chaingun will not be updated, and it will look funny. + +------------------------------------------------------------------------------- + +Q: Why is the music quality so bad? + +A1: You might have a file named default.cfg in the same directory as MBF.EXE. + Allegro considers this a music configuration file. Refer to Allegro docs. + +A2: MBF music on AdLib cards is not the same as Doom, because the FM instrument + definitions are different. You may be able to import .IBK files into + Allegro to improve the music's quality. See the Allegro docs. Also see + snddrvr.txt for other alternatives. + +------------------------------------------------------------------------------- + +Q: Where do the dogs come from? I don't see any wads!!! + +A: Dog sprites & sounds, like many other things, are embedded inside the .exe. + However, you can modify them with wads. See mbfedit.txt for details. + +------------------------------------------------------------------------------- +Lee Killough Last Updated 12/22/98 diff --git a/doc/README.command-line b/doc/README.command-line new file mode 100644 index 0000000..a46bcf0 --- /dev/null +++ b/doc/README.command-line @@ -0,0 +1,735 @@ +BOOM.CFG(5) BOOM.CFG(5) + + + +NAME + boom.cfg, glboom.cfg - Configuration file for PrBoom v2.1.0 onwards + +USAGE + When a version of PrBoom is run, it searches for this configuration + file to modify its default settings. Every time PrBoom exits, it + rewrites the configuration file, updating any settings that have been + changed using the in-game menus. + + PrBoom expects the config file to be ~/.prboom/boom.cfg, or + ~/.prboom/glboom.cfg if compiled with GL support. Alternatively, it can + be made to look elsewhere by using a command-line parameter: + + {prboom,glboom} [-config myconf] + +FORMAT + boom.cfg consists of a number of variables and values. Each line is of + the following format: + + [ { {{#,;,[} comment_text , variable {decimal_integer, 0x hex_integer, + "string_text"}}] + + Any line beginning with a non-alphabetic character is treated as a com- + ment and ignored; for future compatibility you should start comments + with a #, ; or [. Note however that when PrBoom rewrites boom.cfg it + does not preserve user added comments. + + Any line beginning with an alphabetic character is treated as a vari- + able-value pair. The first word (sequence of non-whitespace charac- + ters) is the variable name, and everything after the following block of + whitespace is taken to be the value assigned to the variable. + + Variables not recognised by PrBoom, or which are given an invalid value + or a value of an inappropriate type, are ignored. Warning messages are + given where relevant. + + The variables recognised by PrBoom are described per-section in the + following sections. The sections are informal however; when PrBoom + rewrites the config file it writes in section headings and puts vari- + ables into the relevant sections, but when reading these are ignored. + + +MISC SETTINGS + compatibility_level + PrBoom is capable of behaving in a way compatible with earlier + versions of Doom and Boom/PrBoom. The value given here selects + the version to be compatible with when doing new games/demos. + See README.compat for details. + + realtic_clock_rate + Selects the speed that PrBoom runs at, as a percentage of normal + game speed. Leave at 0 unless you want to experiment. Note that + it is considered `cheating' to use this at any setting below 0 + (or above?). + + max_player_corpse + Sets the maximum number of player corpses to leave lying around. + If this limit would be exceeded, an old corpse is removed. Use- + ful for big/long Deathmatch games, where the sheer number of + corpses could slow the game down. + + flashing_hom + Flag indicating whether a flashing red background is drawn to + highlight HOM errors in levels (for level developers) + + demo_insurance + Selects a method of protecting demos against `going out of sync' + (where the player seems to lose control and behave madly, but in + fact the players original instructions as stored in the demo + have got out of sync with the game he was playing). 0=No protec- + tion, 1=Full protection, 2=Only while recording demos. Safest + when left set to 2. + + endoom_mode + This parameter specifies options controlling the display of the + credits screen when Doom exits. Currently it is the sum of 3 + options: add 1 for colours, 2 for non-ASCII characters to be + displayed, and 4 for the last line to be skipped so the top line + doesn't scroll off screen. + + level_precache + If set, when loading a new level PrBoom precaches all the graph- + ics the level is likely to need in memory. This makes it much + slower to load the level, but reduces disk activity and slow- + downs reading data during play. Most systems are fast enough + that precaching is not needed. + + +FILES SETTINGS + wadfile_1, wadfile_2 + The names of 2 .wad files to be automatically loaded when PrBoom + is started. A blank string means unused. + + + dehfile_1, dehfile_2 + The names of 2 patch files (.deh or .bex) to be automatically + loaded when PrBoom is started (empty string for none). + + +GAME SETTINGS + default_skill + The default skill level when starting a new game. + + weapon_recoil + Enables recoil from weapon fire. + + doom_weapon_toggles + Flag indicating whether pressing 3 or 1 when that weapon is + already selected causes the selected shotgun or fist/chainsaw to + be toggled, as in original Doom. Some people prefer to use a + number for each weapon alone. + + player_bobbing + Enables player bobbing (view randomly moving up/down slightly as + the player runs). + + monsters_remember + Makes monsters remember their previous enemy after killing their + current target. + + monster_infighting + Whether monsters will fight each other when they injure each + other accidentally. + + monster_backing + Whether monsters without close combat weapons will back away + from close combat (unlike original Doom). + + monster_avoid_hazards + Whether monsters avoid crushing ceilings. + + monkeys + Whether monsters will climb steep stairs. + + monster_friction + Whether monsters are affected by changed floor friction (they + should be, but weren't in Boom) + + help_friends + Whether monsters will help out injured monsters by aiding them + against their attacker. + + player_helpers + The number of helper dogs to spawn. + + friend_distance + Distance within which friends will generally stay. + + dog_jumping + Whether dogs will jump. + + sts_always_red + PrBoom can make the colour of the text displays on the status + bar reflect your current status (red=low, yellow=average, + green=good, blue=super-charged). This option if set selects the + traditional Doom behavior of always-red status bar display; set + to 0 to allow the coloured display. + + sts_pct_always_gray + See above, this makes just the percent signs always gray, + instead of changing colour. + + sts_traditional_keys + Doom and PrBoom have two types of keys; PrBoom will normally + display both keys of a given colour if you have both. This + option, if enabled, instead makes PrBoom only ever display one + key of each colour, in the same way Doom did. + + traditional_menu + Changes PrBoom's menu ordering to be the same as original Doom + if enabled. + + show_messages + When enabled, text messages are displayed in the top left corner + of the screen describing events in the game. Can be toggled in + the game, this is just to preserve the setting. + + autorun + Makes the player always run, without having to hold down a run + key. Can be toggled in the game, this just preserves the set- + ting. + + +SOUND SETTINGS + sound_card + Selects whether sound effects are enabled (non-zero enables). + For compatibility reasons with Boom, a range of values are + accepted. + + music_card + Selects whether in-game music is enabled (non-zero enables). For + compatibility reasons a range of values are accepted. + + pitched_sounds + If enabled by this variable, this enables `pitching' (making + pitch adjustments to the playing sounds) for 16 bit sound cards. + + samplerate + The samplerate for soundmixing and timidity. The sound quality + is much better at higher samplerates, but if you use timidity + then higher samplerates need much more CPU power. Useful values + are 11025, 22050, 44100 and 48000. + + sfx_volume + Sound effects volume. This is best adjusted in the game. + + music_volume + Music volume. This is best adjusted in the game. + + mus_pause_opt + Selects what PrBoom does to the music when a games is paused. + 0=stop the music, 1=pause the music (stop it playing, but when + resumed resume it at the same place - not implemented), 2=con- + tinue playing. + + sounddev, snd_channels, soundsrv, musicsrv + These variables are no longer used by PrBoom, but are kept for + compatibility reasons. + + +COMPATIBILITY SETTINGS + These are settings that let you choose whether the normal game mechan- + ics are used, or whether various quirks, bugs and limitations of the + original Doom game are emulated. + + +VIDEO SETTINGS + screen_width, screen_height + For versions of PrBoom which support high-res, these specify the + default screen or window size for PrBoom. These settings are + ignored and preserved by versions of PrBoom which do not do + high-res (they assume 320x200). + + use_fullscreen + If set, this causes PrBoom to try to go full screen. Depending + on your video driver and mode, this may include changing screen + resolution to better match the game's screen resolution. + + use_doublebuffer + Use double buffering to reduce tearing. On some machines this is + even faster than the normal method, but on others this makes + problems, so you have to try out which setting works best. + + translucency + Causes PrBoom to display certain objects as translucent. + + tran_filter_pct + Selects how translucent objects are when they are translucent. + Play with this and see for yourself. + + screenblocks + Selects a reduced screen size inside the PrBoom window (the + player's view is surrounded by a border). Normally this is unde- + sirable, but it can help speed up the game. Can be changed in + the game with the +/- keys, this variable is just to preserve + that setting. + + usegamma + Selects a level of gamma correction (extra screen brightening) + to correct for a dark monitor or light surroundings. Can be + selected in the game with the F11 key, this config entry pre- + serves that setting. + + +OPENGL SETTINGS + If you are knowledgeable about OpenGL, you can tweak various aspects of + the GL rendering engine. + + gl_nearclip + The near clipping plane *100. + + gl_colorbuffer_bits + The bit depth for the framebuffer. (16, 24 or 32 bits) + + gl_depthbuffer_bits + The bit depth for the z-buffer. (16, 24 or 32 bits) + + gl_tex_filter_string + A string, one of the following: GL_NEAREST or GL_LINEAR (no + mipmapping), or one of GL_NEAREST_MIPMAP_NEAREST, + GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LIN- + EAR_MIPMAP_LINEAR with mipmapping. + + gl_tex_format_string + One of the following strings: GL_RGBA - means format selected by + driver (not so good), GL_RGBA2 - means 2 bits for each component + (bad), GL_RGBA4 - means 4 bits for each component (like GL_RGBA + on most cards), GL_RGB5_A1 - means 5 bits for each color compo- + nent 1 bit for the alpha channel (default), GL_RGBA8 - means 8 + bits for each component (best quality, but only a little bit + better than GL_RGB5_A1 and slower on most cards) + + gl_drawskys + If 0, disables drawing skies, which may be needed with some + problematic 3D cards. + + gl_sortsprites + Experimental option, possibly faster but less reliable. + + +MOUSE SETTINGS + This section specifies settings for using a mouse with PrBoom. There + are several settings that control button bindings (what action each + button causes in the game); these are easiest set from the in-game + menus, these config entries are to preserve the settings between games. + + use_mouse + Enable or disable the use of a mouse with PrBoom. + + mouse_sensitivity_horiz, mouse_sensitivity_vert + Sets the sensitivity of the mouse in PrBoom. Easier set from the + in-game menus. + + +KEY BINDINGS + These specify the keys that trigger various actions in PrBoom. The + codes used for keys are internal to PrBoom, though many keys are repre- + sented by their ASCII codes. It is easiest to modify these via the in- + game menus (OPTIONS->SETUP->KEY BINDINGS). These config file entries + preserve the settings from this menu between game sessions. + + +JOYSTICK SETTINGS + There are the trigger variables here, which are calculated during joy- + stick calibration (the values received from the kernel driver outside + of which movement is caused in the game). Also there are the button- + bindings, again best adjusted using the in-game menus. + + use_joystick + This selects the number of the joystick to use, or 0 selects no + joystick. You have to have the relevant device files (/dev/js0 + etc) and the kernel driver loaded. + + +CHAT MACROS + These are pre-written text strings for quick transmission to players in + a network game (consult your Doom documentation). Easiest set via the + in-game menus (OPTIONS->SETUP->CHAT MACROS). + + +AUTOMAP SETTINGS + These are settings related to the automap. These are easiest set from + within the game. + + +HEADS_UP DISPLAY SETTINGS + These are settings related to the heads-up display, that is messages + received while playing and the heads-up display of your current status + obtained by pressing + while the view is full-screen in PrBoom. See the + Boom documentation for details. All controlled best from within the + game. + + +WEAPON PREFERENCES + Here are the settings from the Weapons menu in the game + (OPTIONS->SETUP->WEAPONS). + + +ALSO SEE + prboom(6), PrBoom's documentation (including the Boom and MBF documen- + tation) and your Doom documentation. + + +AUTHOR + See the file AUTHORS included with PrBoom for a list of contributors to + PrBoom. This config file reference written by Colin Phipps + (cph@moria.org.uk). + +PRBOOM-GAME-SERVER(6) PRBOOM-GAME-SERVER(6) + + + +NAME + prboom-game-server - Server for network games of PrBoom. + +SYNOPSIS + prboom-game-server [ -adfnrv ] [ -e epis ] [ -l level ] [ -t ticdup ] + [ -x xtics ] [ -p port ] [ -s skill ] [ -N players ] [ -c conffilename + ] [ -w wadname[,dl_url ]] + +DESCRIPTION + PrBoom is a version of the 3D shoot'em'up Doom, originally by id soft- + ware. It includes, amongst other things, the ability to play with sev- + eral players connected by a tcp/ip network. prboom-game-server is the + `server', that is the program that passes data between the different + players in the game. + + To start a network game (often abbreviated to `netgame'), first the + server is started. prboom-game-server accepts various parameters to + control the type of game (the skill level, number of players, level to + play, optional WAD file(s) to load, etc). + + Then each player that wishes to participate runs prboom -net hostname, + where hostname is the name of the machine on which the server is run- + ning. Each copy of prboom retrieves information about the game from the + server, and when the specified number of players have joined, the game + begins. + + +Options + -N players + Specifies the number of players in the game (default 2). The + server will wait for this many players to join before starting + the game. + + -e epis + The episode to play (default 1). Unless you are playing Doom 1 + or Ultimate Doom, and wish to play one of the later episodes, + you do not need to change this. + + -l level + The level to play (default 1). + + -s skill + Specify the skill level to play (1-5). + + -d Set game mode to (old) deathmatch (default is cooperative). See + the original Doom docs for information about the different net- + work game modes. + + -a Set game mode to `altdeath' (v2 deathmatch) (default is coopera- + tive). See the original Doom docs for information about the dif- + ferent network game modes. + + -f Select fast mode (monsters move faster). + + -n Selects nomonsters mode, i.e. there are no monsters in the game. + + -r Respawn mode. If you don't know what this is, you don't want to + ;-). + + -c conffilename + Specifies a configuration file to read which sets parameters for + the game. This is in the same format as the PrBoom configuration + file (in fact, you can ask it to read your normal PrBoom config- + uration file if you want). Only certain settings are acknowl- + edged: default_skill, default_compatibility_level, the compati- + bility options and some of the game settings (use -v to have the + server print the options as it recognises them). + + -w wadname[,dl_url] + Specifies a WAD file to play. This is added to the internal list + that the server keeps. When a client connects, the server sends + the list of WADs; PrBoom will then add this to the list of WADs + specified on its command line. Optionally, an url to the file + can be given too; if when PrBoom connects it cannot find the + named WAD, it will attempt to retrieve the file from the given + url, extracting it if necessary. See prboom(1) for information + about the supported url types and compression formats. + + -t ticdup + Reserved. + + -x xtics + This causes extra information to be sent with each network + packet; this will help on networks with high packet loss, but + will use more bandwidth. + + -p port + Tells prboom-game-server what port number to communicate via + (default 5030). Note that if you change this from the default, + then all the clients will also need to specify this number when + they try to connect (the default programmed into prboom is also + 5030). + + -v Increases verbosity level; causes more diagnostics to be + printed, the more times -v is specified. + +More Information + prboom(6), boom.cfg(5) + + For more information, see the README that came with PrBoom. + + Doom is a registered trademark of id software (http://www.idsoft- + ware.com/). + +Author + See the file AUTHORS included with the PrBoom distribution. This man + page was written by Colin Phipps (cph@moria.org.uk). + + + +PRBOOM(6) PRBOOM(6) + + + +NAME + prboom - PrBoom, a version of Doom for Unix, Linux and Windows systems + +SYNOPSIS + prboom [ -complevel lvl ] [ -width w ] [ -height h ] [ -viewangle n ] + [ -vidmode gl ] [ -[no]fullscreen ] [ -[no]window ] [ -iwad iwadname ] + [ -file wad1 ... ] [ -deh deh_file ] [ -noload ] [ -loadgame + {0,1,2,3,4,5,6,7} ] [ -warp { map | epis level } -skill {1,2,3,4,5} ] + [ {-fastdemo,-timedemo,-playdemo} demofile [ -ffmap num ] ] [ -record + demofile ] [ -solonet ] [ -net hostname[:port] ] [ -deathmatch [ -alt- + death ] ] [ { -timer mins | -avg }] ] [ -nosound ] [ -nosfx ] [ -nomu- + sic] [ -nojoy ] [ -nomouse ] [ -noaccel ] [ -nodraw ] [ -config myconf + ] [ -save savedir ] [ -bexout bexdbg ] [ -debugfile debug_file ] [ + -devparm ] [ -noblit ] [ -nodrawers ] + +DESCRIPTION + PrBoom is a version of the 3D shoot'em'up Doom, originally by iD soft- + ware. It is based on Boom, a version of Doom adapted by TeamTNT + (http://www.teamtnt.com/) for DOS. PrBoom uses the SDL library, meaning + it can run on a variety of different systems, including Windows, X11, + Linux/SVGALib. + +Options + -complevel lvl + This sets the compatibility mode that PrBoom runs in. If you + need to change this, see README.compat. + + +Video Options + -width w + Specifies the width of the PrBoom window, in pixels. Default is + 320, the width must be greater than 320.. + + -height h + Specifies the height of the PrBoom window, in pixels. Default is + 200, the height must be greater than 200. + + -viewangle n + Causes the player view to be rotated by a given offset (speci- + fied in 45degree increments, in the range 0..7) from the way the + player is facing. + + -vidmode gl + Use the OpenGL video mode. The default is to use the software + video mode. + + -fullscreen, -nofullscreen + These options toggle fullscreen mode. The default is fullscreen. + + -window, -nowindow + This pair of options also toggle fullscreen mode. They only take + effect for this prboom session and do not alter your configura- + tion file. + +WAD Options + -iwad iwadname + Specifies the location of the IWAD file, typically doom.wad or + doom2.wad (or doom2f.wad). This tells prboom where the main .wad + file that came with the version of Doom that you own is. + + -file wad1 ... + Specifies a list of PWAD files to load in addition to the IWAD + file. PWAD files modify the existing Doom game, by adding levels + or new sounds or graphics. PWAD files are widely available for + download; try ftp.cdrom.com/pub/idgames for starters. + + -deh deh_file + Tells PrBoom to load the dehacked patch deh_file. + +Game Options + -loadgame {0,1,2,3,4,5,6,7} + Instructs PrBoom to load the specified saved game immediately. + + -warp { map | epis level } + Tells PrBoom to begin a new game immediately. For Doom 1 or + Ultimate Doom, you must specify the episode and level number to + begin at (epis is 1 for Knee-Deep in the Dead, 2 for Shores of + Hell, 3 for Inferno, 4 for Ultimate Doom; level is between 1 and + 9). For Doom ][ or Final Doom, you must specify the map to begin + at, which is between 1 and 2 (0 for German Doom ][). + + -skill n + Tells PrBoom to begin the game at skill level n (1 for ITYTD, 2 + for Not Too Rough, 3 for Hurt Me Plenty, 4 for Ultraviolent, 5 + for Nightmare). + + -respawn + Tells PrBoom that monsters that die should respawn (come back to + life) after a while. Not for the inexperienced. + + -fast Tells PrBoom to make all the monsters move react faster. Not + for the inexperienced. + + -nomonsters + Tells PrBoom to include no monsters in the game. + +Multiplayer Options + -net hostname[:port] + Specifies that a TCP/IP network game is to be started. hostname + is the name of the machine on which the network game server is + running (prboom-game-server). For more information about this, + see prboom-game-server(6) and the README that came with prboom. + port is the port number on the remote machine to which to con- + nect; if not specified, the default of 5030 (which is the + default for prboom-game-server(6) ) is assumed. The server will + configure your PrBoom settings, so that all the players have the + same game settings (skill, map etc). + + Also, the server may specify additional PWAD files to play with; if you + do not have the required .WAD file, PrBoom will ask the server for a + download path, and attempt to use wget(1) and if necessary unzip(1) to + download and extract the required WAD. + + -port portnum + Specifies the local port to use to communicate with the server + in a netgame. + + -deathmatch + No longer used. Tells PrBoom to begin a deathmatch game, but + this is overridden by the server's settings. Only works for sin- + gle play (!). + + -altdeath + Similar to -deathmatch, but implies a different set of rules for + the deathmatch game. No longer used (specified by the server). + + -timer mins + No longer used. Specifies that levels will end after mins min- + utes of play if the level is still being played, but is overrid- + den by the server in a netgame. Not really useful for single + play. + + -avg Equivalent to -timer 20 + + -solo-net + Used to run a single-player network game, without a network game + server. This enables network game items & options for an other- + wise single-player game; some demos are recorded like this. + +Demo (LMP) Options + -record demofile + Instructs PrBoom to begin recording a demo, to be stored in + demofile.lmp. You should specify game options to specify which + level and skill to record at. + + -playdemo demofile + Play the recorded demo demofile.lmp + + -timedemo demofile + Play the recorded demo demofile.lmp, reporting information about + the length of the demo (in gametics) afterwards. + + -fastdemo demofile + Play the recorded demo demofile.lmp as fast as possible. Useful + for benchmarking PrBoom, as compared to other versions of Doom. + + -ffmap num + Fast forward the demo (play at max speed) until reaching map num + (note that this takes just a number, not a map name, so so + -ffmap 7 to go fast until MAP07 or ExM7). + +I/O Options + -nosound + Disables all sound effects and in-game music. This prevents the + sound server loading, which lets the game run a little faster. + + -nosfx Disables sound effects during the game. This does not stop the + sound server loading, however, so for best performance use + -nosound. + + -nomusic + Disables playing of music in the game. + + -nojoy Disables joystick support. + + -nomouse + Prevents the mouse being grabbed by the prboom window. + + -noaccel + For prboom, this prevents it using the MITShm server extension + for passing the screen data to the X server. This option may be + required if the X server is not local. For lsdoom, this tells + lsdoom not to use the accelerated graphics functions that + SVGALib provides even when they are supported for your video + card (normally this is autodetected). + + -1, -2, -3 + Specifies the scale factor by which to enlarge the window. The + default, -1, displays the normal 320x200 pixel Doom screen (or + whatever size is specified by the -width and -height parameters + or in the config file for prboom). If this window is too small, + try using -2 or -3 to enlarge the window. -nodraw Suppress all + graphical display. Only for debugging & demo testing. + +Configuration + -config myconf + Loads an alternative configuration file, named myconf. The + default is boom.cfg(5), taken from the same directory as PrBoom + was run from, except when running with OpenGL, then the default + is glboom.cfg(5). + + -save savedir + Causes prboom to save games in the directory specified by + savedir instead of ~/.prboom/. + +Debugging/Profiling Options + -devparm + Development mode. Mostly redundant these days, but it does force + non-lazy generation of texture lookups which can be useful for + level authors debugging PWADs. + + -debugfile debug_file + Causes some debugging information, mainly network info I + believe, to be written to the named file as prboom runs. + + -nodrawers + Causes no rendering to be done. The only conceivable use of this + is (a) a multiplayer server (b) to test the speed of the other + routines in the program, when combined with -timedemo. + + -noblit + Causes no copying to the screen from the rendering buffer to be + performed. The only conceivable use of this is (a) a multiplayer + server (b) to test the speed of the other routines in the pro- + gram, when combined with -timedemo. + + -bexout bexdbg + Causes diagnostics related to bex and dehacked file processing + to be written to the names file. + +More Information + wget(1), unzip(1), boom.cfg(5), prboom-game-server(6) + + For more information, see the README that came with PrBoom, the Boom + documentation, and your original Doom documentation. + + Doom is a registered trademark of id software (http://www.idsoft- + ware.com/). + +Author + See the file AUTHORS included with the PrBoom distribution. + + + + local PRBOOM(6) diff --git a/doc/README.compat b/doc/README.compat new file mode 100644 index 0000000..b1ba2ec --- /dev/null +++ b/doc/README.compat @@ -0,0 +1,93 @@ +PrBoom compatibility options +============================ + +Some of this is a bit dry. You might want to skip to the bottom and read the +examples. + +Internally, PrBoom maintains a "compatibility level" which it uses to +determine how to behave in various circumstances. The supported levels are +currently: + +Number Name Description +0 doom_12_compatibility Emulate Doom v1.2 game details where possible +1 doom_1666_compatibility Behave like Doom v1.666 as far as possible +2 doom2_19_compatibility As compatible as possible for playing original + Doom demos +3 ultdoom_compatibility Ultimate Doom made a few changes +4 finaldoom_compatibility Compatible with Final Doom & Doom95 - needed + for some demos recorded with Final Doom doom2.exe +5 dosdoom_compatibility Emulate early dosdoom versions + (some TASdoom demos work with this) +6 tasdoom_compatibility unused +7 boom_compatibility_compatibility Emulates Boom's compatibility mode +8 boom_201_compatibility Emulates Boom v2.01 +9 boom_202_compatibility Emulates Boom v2.02 +10 lxdoom_1_compatibility Emulates LxDoom v1.4.x +11 mbf_compatibility Emulates MBF +12 prboom_1_compatibility Emulates PrBoom v2.03beta +13 prboom_2_compatibility PrBoom v2.1.0 +14 prboom_3_compatibility PrBoom v2.1.1-2.2.6 +15 prboom_4_compatibility PrBoom v2.3.x +16 prboom_5_compatibility PrBoom v2.4.0 +17 prboom_6_compatibility Current PrBoom + +You can cycle through the compatibility levels in the game using the TNTCOMP +cheat. There's also the default_compatibility_level config file option, and +the -complevel command line parameter. + +The numbers are subject to change between versions, so if you're doing +elaborate stuff with these things you're advised to check this file each +time you upgrade. Most people should just leave default_compatibility_level +set to -1 in the config file, which means "use most recent" which has most +features, most bug fixes, etc. + +But some people want to test the behaviour of levels with older versions, and +will find it helpful to not have to load a dozen games to do so. Some people +like me play a lot of old levels, so need to be able to enable compatibility +with some old bugs. And some people may want to record demos for older game +versions. + +When you play a demo, PrBoom sets the compatibility level and settings +automatically. When you load a savegame, the settings are restored. + +When you start a new game, the compatibility level is got from (in order of +preference): +- -complevel parameter +- default_compatibility_level config file param if not -1 +- set to the most recent otherwise + +If the compatibility level is MBF or better, the detailed compatibility +settings are read from the comp_* config file options, as in MBF. Otherwise, +the settings are put to the defaults for that game version. + +You can adjust the compatibility settings during play from the menus +(Options->Setup->Doom Compatibility). + +The sort of people interested in these things will already know the MBF +options, so I'll just list the changes: +- comp_floors now also causes the original Doom behaviour of objects stuck in +a ceiling preventing the floor below lowering. +- new option comp_moveblock enables the old movement clipping bug which +allows things to go through walls sometimes (mancubus shots, and key +grabbing tricks) + +PrBoom can also record old demos with some success (demo players see the +comp_moveblock note above): + +prboom -complevel 2 -record test + +records a doom v1.9 compatible demo + +prboom -complevel 9 -record whatever + +records a Boom (v2.02) demo + +prboom -complevel 11 -record blah + +records an MBF demo. + +Of course, demo recording is no more reliable than demo playback; original +Doom demos will usually but not always work. MBF support should be perfect. + +- Colin + diff --git a/doc/README.demos b/doc/README.demos new file mode 100644 index 0000000..90487f8 --- /dev/null +++ b/doc/README.demos @@ -0,0 +1,54 @@ +PrBoom demo support +=================== + +PrBoom has lots of demo options compared to most Doom ports, so I decided to +write a brief document outlining the features. If you're thinking of +recording demos, you're strongly advised to read this. + +To play demos, you of course use "prboom -playdemo demoname" (the .lmp is +optional). To the best of my knowledge, PrBoom should play all MBF and new +PrBoom demos perfectly. It also plays most Doom v1.9 demos (significantly more +than Boom or MBF); almost all demos recorded with ultimate doom or doom2.exe +version v1.9 should work (please let me know if you find ones that don't), but +there are still problems with doom95 and final doom though. It should play most +Boom v2.01 and v2.02 demos, although I don't know of that many to test with. It +will also try to play older Doom demos, LxDoom demos, and Boom v2.00 demos. + +Recording demos is done with "prboom -record demoname". **IMPORTANT** PrBoom +will record to a number of demo formats, namely Doom v1.9, Boom v2.02, MBF, +and PrBoom - which format it records is determined by your current +compatibility level, see README.compat. The purpose of this feature is so +people like me that won't let DOS anywhere near their computers can record +demos compatible with other engines. + +PrBoom supports the -recordfrom parameter, which starts recording a demo +from a given savegame. "-recordfrom demoname n" is a synonym for "-record +demoname -loadgame n", which records a demo from savegame number n. + +People doing speedrun demos may be interested in the comp_moveblock option, +see README.compat. + +PrBoom supports loading and saving games during demo recording and playback +(but only for new PrBoom demos, not for older demo formats, obviously). +Don't worry, saves in demo playback are written with different names, they +don't overwrite your own saves. Saves when recording, do. You can load games +during recording, but they'd better be games saved during the same +recording, otherwise it won't play back. + +PrBoom supports continuation of recorded demos. This is the scenario this +feature is designed for: say you're beta testing a level for someone, and +you want to send them a *complete* demo of how the level went (saves, deaths +and reloads and all). You start with "prboom test.wad -record test", play +for half an hour, save and exit. Next day, you go to play again, with +"prboom test.wad -record test" again. PrBoom will scan the existing +test.lmp, find the savegame command, and insert a loadgame command to reload +that game; it will reload the game and continue recording of the demo. This +is an experimental feature, but hopefully it works :-). + +**IMPORTANT** The observant will have noticed from the above that PrBoom +won't overwrite existing demos anymore. If you want to record over a demo, +delete it first. + +I think that's all for now. + +- Colin diff --git a/doc/boom.txt b/doc/boom.txt new file mode 100644 index 0000000..d6c9877 --- /dev/null +++ b/doc/boom.txt @@ -0,0 +1,1269 @@ +===================================================================== + BOOM.TXT 10/18/98 +===================================================================== + +BOOM v2.02 --- a modified port of the released DOOM source + +Final Phase 1 Release + +By TeamTNT + +Programmers: + +Lee Killough, Jim Flynn, Rand Phares, Ty Halderman, and Stan Gula + + +DoomSource Members: + +Andre Majorel Paul Schmitz +Bob Evans (odessa) Richard Nagel (*Weeds*) +Chris Couleur Steve McCrea +Lisa Moore (pup) William.D.Whitaker +Adam Landefeld Dave Brachman +Dave Armstrong Jeremy Wagner + + +Demo Wads: + +Gary Gosson (Dawning), Paul Fleschute (Rage) + +===================================================================== + +Contents + +Section 1. Installing BOOM +Section 2. Configuring BOOM +Section 3. Playing BOOM in Single Player mode +Section 4. Playing BOOM in Serial/Network mode +Section 5. Editing for BOOM +Section 6. Differences between BOOM and DOOM +Section 7. Files in the BOOM Distribution +Section 8. How to report bugs in BOOM +Section 9. Acknowledgements + +===================================================================== +--------------------------- +Section 1. Installing BOOM +--------------------------- + +Requirements: +------------- + +BOOM requires a minimum of a 486DX/33 with 8M of RAM running DOS. BOOM will +run under Win'95 in a DOS Window, but 16M RAM is recommended. + +Recommended minimum specs are 486DX/66 with 16M or Win'95 with 32M + +BOOM requires a copy of DOOM, DOOM II, Ultimate DOOM, or Final DOOM already + installed on your system. In the installation instructions below we use + C:\DOOM2 to denote the directory it is installed in. Substitute the + path to DOOM on your system wherever that appears. + +BOOM requires a sound card, it will not play sounds over the PC speaker. If +you do not have a sound card you MUST run it with the -nosound option on the +command line. + +Suggested installation procedure: +--------------------------------- + +1) Unzip the download archive in a new directory, which we will call \BOOM. + +2) Type BOOM -iwad C:\DOOM2, the game will start as usual. To avoid having + to type -iwad C:\DOOM2, add the line below to your AUTOEXEC.BAT and + reboot: + + SET DOOMWADDIR=C:\DOOM2 + + Alternately you can unzip the BOOM archive in your DOOM directory and + avoid needing -iwad or the change to your AUTOEXEC.BAT file. + + You can also simply copy DOOM1.WAD, DOOM.WAD, or DOOM2.WAD to the BOOM + directory. + +3) BOOM provides support for joysticks. With the current release, your + joystick must be calibrated using the Allegro setup utility ASETUP.EXE. + + 3a) run the Allegro setup utility ASETUP.EXE + 3b) press the Joystick button + 3c) follow the on-screen instructions to calibrate your joystick + 3d) when finished, exit, saving configuration + +4) If sound and music sound ok, and the mouse and joystick behave properly, + you're done! + +Sound/Music Troubleshooting +--------------------------- + +5) Make sure there is a BLASTER= line in your environment (type SET to see +the environment in a DOS session or window). If this is missing, consult +your sound card documentation or technical support. There is a brief +description of the line and its parameters in SNDDRVR.TXT. + +6) If you have a plug and play sound card and are running under DOS, you +need to make sure you install plug and play support in DOS; see SNDDRVR.TXT. + +7) If your AWE32 does not produce music or sound under DOS make sure you +have an E parameter in your BLASTER= line. E620 is the default. + +8) Run the Allegro setup utility ASETUP.EXE + 8a) press the Autodetect button. + 8b) press the Test button. + 8c) if the test pieces all sound correct exit, saving configuration, else + 8c1) go back to the main menu and press digi_driver to configure sound + effects, or midi_driver to configure music. + 8c2) in the menu of device drivers shown, the ones checked are + possibles. Try them in bottom to top order, running the Test + screen after each until the best results are obtained. Exit, + saving configuration. + 8d) open ALLEGRO.CFG with a text editor like Notepad and note the + settings for digi_card and midi_card. Close it and open + BOOM.CFG. Transfer the digi_card setting to sound_card and the + midi_card setting to music_card in BOOM.CFG. Exit. + +9) In the ASETUP.EXE program's sound test screen, the left and right buttons +are reversed relative to some sound cards. If the sounds in DOOM seem to be +reversed to you, then change the flip_pan setting in ALLEGRO.CFG from 0 to 1 +or vice-versa. + +Mouse Troubleshooting +--------------------- + +10) If the mouse fails to work, left motion seems to wait until a button is +clicked, change the line mouse=logitech in ALLEGRO.CFG to mouse=ms, or add +the line mouse=ms at the top. + + +---------------------------- +Section 2. Configuring BOOM +---------------------------- + +Using the Setup Menus +--------------------- + +BOOM provides Setup Menus in the game itself to ease the task of setting +configuration options. From the Main Menu, select 'Setup' and you'll see the +following menu: + + Key Bindings + Weapons + Status Bar / HUD + Automap + Enemies + Messages + Chat Strings + +Selecting a menu item will present a screen or set of screens from which +you can set many of the BOOM options. + +Note: In general, and by default, the cursor movement keys are used to + navigate the menu screens. Exceptions will be noted where appropriate + below. If you bind 'menu navigation' keys (i.e. 'down arrow') to + something other than the default settings, you'll need to think 'new key' + when reading 'default key' in the following text. + + The mouse and joystick may also be used to navigate the menus. + +Note: If a joystick is connected, it's possible that the system may receive + false left or right movements when the joystick is idle. This can affect + any Setup menus that have multiple screens. If this occurs (sudden switch + to previous or next menu) you'll need to recalibrate the joystick, which + will make the problem go away. Refer to the calibration instructions in + Section 1. + +1) Key Bindings + +*Multiple Screens* + +There are four Key Binding screens. You can move among them using the left +and right arrow keys. If "NEXT->" appears in the bottom right corner, +there's another screen. + +*Menu Items & Binding* + +Each menu item shows the action on the left and the key bound to it on the +right. If you want to bind an action to a different key, move the 'cursor' +(where the action is ORANGE) to that action. Press ENTER, the action turns +WHITE, and you're ready to rebind that action. + +Pressing ANY KEY will IMMEDIATELY bind that key to the selected action. + +CAVEAT: + +Be careful when rebinding keys you use to navigate the menus. If you were to +change the 'next item' key from 'DOWN ARROW' to 'Z' then you'll need to use +the 'Z' key from that point on to move to the next menu item. + +*Binding Multiple Keys to an Action* + +In this release of BOOM, only one key can be bound to an action. In a future +release, you will be able to bind multiple keys to an action. + +*Binding a Key to Multiple Actions* + +Under certain circumstances, you can bind a key to several actions. All +actions are grouped according to game state, which can be 'normal play', +'in the menu screens', 'in chat mode', and 'looking at the automap'. + +If two actions are in the same group, they can't have the same key binding. +If two actions are in different groups, they can. + +For example, you can't bind F1 to both the 'HELP' and 'SAVE GAME' actions +because both are available during normal play. If F1 is bound to 'HELP' and +you rebind it to 'SAVE GAME', then whatever key was bound to 'SAVE GAME' +will move over and be bound to 'HELP'. + +As another example, you can bind the 'H' key to 'HELP' for use in normal +play, and to 'GRID' for use when you have the automap up. Some +experimentation will show you where you can bind the same key to various +actions. + +*Mouse Buttons* + +Where an action shows a Mouse Button setting (MB1,MB2,MB3), that action can +be bound to a Mouse Button. When the action is selected for rebinding, +pressing the desired Mouse Button will bind the action to that button. + +A future release of BOOM will allow you to set Mouse Buttons to any action. + +*Joystick Buttons* + +Where an action shows a Joystick Button setting (JSB1,JSB2,JSB3,JSB4), +that action can be bound to a Joystick Button. When the action is selected +for rebinding, pressing the desired Joystick Button will bind the action +to that button. + +A future release of BOOM will allow you to set Joystick Buttons to any action. + + +2) Weapons + +*Weapon Recoil* + +When ON, your weapon will provide you with a little 'kick'. Great fun when +on an icy floor. When OFF, there is no recoil. Move the cursor to this item +and press ENTER twice to change it. At the moment, you must start a new +level in order for this change to take effect. + +*Player Bobbing* + +When ON, your weapon will bob back and forth, up and down, according to your +movement. When OFF, there's no movement. Move the cursor to this item and +press ENTER twice to change it. At the moment, you must start a new level in +order for this change to take effect. + +*Weapon Preferences* + +In BOOM, you can declare which weapon you want to grab when you run out of +ammo for your current one. To change the ordering, move to an item, select +it with ENTER, then press a number between 1 and 9, where + + 1 = FIST + 2 = PISTOL + 3 = SHOTGUN + 4 = CHAINGUN + 5 = ROCKET LAUNCHER + 6 = PLASMA RIFLE + 7 = BFG + 8 = CHAINSAW + 9 = SUPER SHOTGUN + +You can select only those weapons that are available in the version of the +DOOM IWAD that you're using. Unavailable weapons will not be shown. + +Note this behavior: If Weapon 2 is in the '1st CHOICE WEAPON' slot and +Weapon 5 is in the '4th CHOICE WEAPON' slot, and you select and change +'1st CHOICE WEAPON' to Weapon 5, then Weapon 2 moves down to '4th CHOICE +WEAPON'. In short, they swap places. + + +3) Status Bar / HUD + +*Red Numbers* + +When ON, the AMMO, HEALTH, and ARMOR numbers on the Status Bar will all be +red. When OFF, these numbers will display in red, yellow, green, and blue, +depending on their values. + +*% Always Gray* + +When ON, the percent signs for HEALTH and ARMOR will always be gray. When +OFF, the percent signs will be the same color as the HEALTH and ARMOR +values. + +*SINGLE KEYS* + +When ON, you'll only see a single key on the Status Bar even if you have the +same color key from both the card and skull sets. When OFF, you'll see two +key icons when you have both keys of the same color. + +*HIDE SECRETS* + +One feature of the new Heads-Up Display is the ability to show your +KILLS/ITEMS/SECRETS stats as you play, instead of waiting until the level is +over. If HIDE SECRETS is YES, these are hidden from you. If NO, they'll be +shown. + +*MESSAGE BACKGROUND* + +When multiple messages are displayed, if MESSAGE BACKGROUND is ON, there is +a solid background behind the messages. If OFF, the messages are overlayed +on whatever action is taking place behind them. + +*# MESSAGE LINES* + +You can set the number of lines for your overall message display from 1 to +16. Move the cursor to this item and press ENTER. Now you can enter a number +w/in the range. If you attempt to enter something outside the range, the key +that would put you outside is ignored. I.e. If you wanted to set this item +to '20', you'd start with a '2', but the following '0' would be ignored. +Press ENTER again to show you're done entering the value. If you don't like +what you entered ("Hey, 2 is not right!"), then re-enter the correct value. + +*HEALTH LOW/OK* + +If you're using red/yellow/green/blue colors for your stats, they indicate +LOW/OK/GOOD/EXTRA AMMO, HEALTH, and ARMOR. 'HEALTH LOW/OK' lets you set +where yellow changes to red (or where OK changes to LOW). + +This value can range between 0 and 200. + +*HEALTH OK/GOOD* + +Similar to the previous item, but this is where green changes to yellow (or +where GOOD changes to OK). + +*HEALTH GOOD/EXTRA* + +Similar to the previous item, but this is where blue changes to green (or +where EXTRA changes to GOOD). + +*ARMOR LOW/OK* +*ARMOR OK/GOOD* +*ARMOR GOOD/EXTRA* + +Same as the "HEALTH" set, but pertains to ARMOR. + +*AMMO LOW/OK* +*AMMO OK/GOOD* + +Similar to the "HEALTH" and "ARMOR" sets, but there's no EXTRA setting, and +the values should be in the range 0 to 100. + + +4) Automap + +This is a collection of color settings for the items that are drawn on the +Automap (background, lines, Things, etc.). + +Each item's color can be changed as follows: + + a) Move the cursor to the item and press ENTER. + + b) A color palette will come up, with a highlight box around the item's + current color. Using the menu movement keys (or mouse), move to the new + color and press ENTER (or click MB1). + + c) The new color is assigned to that item. + + d) Setting a new feature to the first color, the one at upper left in the + array of colors, disables the feature, in case you don't want the extra + information. If you actually want to set the color black for a new + feature use the black in the middle of the bottom row of colors, color + 247. + +5) Enemies + + Includes config options that have to do with enemies. + + +6) Messages + + Includes config options that have to do with messages. + + For items denoting string color, the value will display in the + appropriate color. + + +7) Chat Strings + + You can change the chat strings using this screen. + + To change a chat string, select it and press Enter. You can edit the + string using overstrike, DEL, backspace, and you can position the + cursor (black underline) using the left and right arrow keys. + + The length of the chat string is limited to what can be shown on this + screen, to prevent overflow. + + When you're finished editing, press Enter again. + + +8) Reset to Default Values + + A "Reset to Default Values" button appears in the upper-right-hand + corner of each Setup screen (and on the first screen where there are + multiple screens). When selected, this button blinks. + + This button allows you to reset all options on this Setup screen (or + set of screens) to their default values. This is useful if you've + made a number of changes that don't seem to work together, and you'd + like to get back to a baseline. + + Pressing Enter will give you a dialogue box that asks you whether you + 'really' want to reset to the default values. Pressing 'N' exits the + dialogue without resetting. Pressing 'Y' resets all values. + +Note: Each screen has its own reset button. There is no single reset + button that covers _all_ options that appear in all Setup screens. If + you want to reset _all_ options to their default values, you'll need + to activate each button separately. + + + +Editing the BOOM.CFG file +-------------------------- + +You can also set BOOM options by editing the BOOM.CFG file with a text +editor like Notepad. You should not use a word processor like Word or +WordPerfect. + +By default each setting is preceeded by a comment that shows the possible +range of the parameter and its default value, as well as text briefly +describing its function. If you do not have these comments, set the +config_help parameter to value 1 and they will appear after the next +time you run BOOM. Conversely, to disable them, set config_help to 0. + +You can only modify the value on each line, not the comment, nor the +variable's name. If you mess up, you can always delete BOOM.CFG and it +will be rewritten with all default values the next time BOOM is run. + +Alternate BOOM.CFG file +----------------------- + +You can keep several BOOM.CFG files on your system, named as you please, +for example MYBOOM.CFG, and select the one you want with the command line +option: -config MYBOOM.CFG. If several users share the same setup, this can +be handy. + +---------------------------------------------- +Section 3. Playing BOOM in Single Player mode +---------------------------------------------- + +BOOM play features +------------------- + +*No Static Limits* + +While wad authors are the ones most interested in BOOM's lack of limits, +players will also benefit by never getting a "Visplane Overflow" or "Too +Many Plats" message followed by a crash. There will be no 2S HOM's even in +terrifically detailed areas, and there is no savegame limit. Other less +commonly known limits have also been removed. + +*Heads Up Display* + +There is now an in-game heads-up-display (HUD) (Press F5, and keep pressing +it until you like the display). This shows kills/items/secrets counts just +like the end-level screen, either keys or frags depending on if you're +single-play or deathmatch, and weapons, ammo, armor and health. The HUD +only displays when in full screen view. Press + in full screen to toggle it +on and off. + +The past N messages can be reviewed by pressing Enter. By default N is 1, +set hud_msg_lines to a number between 2 and 16 to enable the feature. Set +hud_list_bgon to 1 for a solid background for the list of past messages, to +0 for a transparent display. Pressing Enter again returns the display to +showing the last message only. + +The display of kills/secrets/items can be suppressed by setting hud_nosecrets +to 1, if you prefer the suspense. For both the HUD and the status bar, colors +are used to indicate transitions to low levels of ammo, health, and armor. +The levels at which transitions occur can be customized thru the health_green, +health_yellow, health_red, armor_green, armor_yellow, armor_red, ammo_yellow, +and ammo_red variables. Note that the weapons line on the hud shows the ammo +level of each weapon possessed thru its color. If you are berserk the fist +and chainsaw show in green, not white. If you prefer red numbers only on the +status bar, set the sts_always_red variable to 1. If you don't like seeing +doubled keys on the status bar when you have both card and skull set +sts_traditional_keys to 1. + +*Better Key Support* + +The F1 screen has been redone to show the new key features (among which are +/ for 180 turns by keyboarders, and capslock for autorun) and it shows the +currently CONFIGURED key settings, not just the defaults. + +Nearly every key recognized by BOOM is reconfigurable thru the BOOM.CFG +file, not just some. A utility KEYCODE is provided to determine the config +file codes for keys. + +*Enhanced Automap* + +The automap has been enhanced. You can now see secrets, teleporters, +closed/open doors, keyed doors, and can suppress shadow lines and triggers +when in IDDT if you wish. All features can be configured off thru setting +them to -1 in the BOOM.CFG file. In addition the players current x,y,z +coordinates are available at screen upper right for being exact in those +playtest reports. + +*Weapon Preferences* + +Full control thru the config file of the order in which weapons are selected +when automatically switching from one to another. + +*Save Games* + +Two more save slots, no such thing as "Save game overflow", game state +completely restored, saves redirectable to directory of your choice from +the command line. + +*More Cheats* + +TNTCOMP to toggle DOOM v1.9 compatibility on or off + +TNTKEYxy to add or subtract a specific key, xy=BC BS RC RS YC YS + +TNTAMMOx to add or subtract a particular kind of ammo. Abbrev.: TNTAMO + +TNTWEAPx to add or subtract a particular weapon + +TNTEM to massacre all living monsters + +TNTTRAN to toggle translucency enable + +TNTFAST to toggle -fast mode + +TNTICE to toggle ice/mud effects on or off + +TNTPUSH to toggle wind effects on or off + +TNTSMART to toggle whether monsters remember their last enemy (you) + +TNTPITCH to toggle variable pitched sound effects on or off + +TNTHOM to highlight areas containing HOM + +TNTKA obtains all keys without adding ammo + +*Better Cheats* + +IDBEHOLDx to turn on light-amps, radsuits, invisibility, etc. until toggled +off + +IDMUSx now works across netgames and is remembered by savegame + +ID[K]FA now includes backpack + +*Fewer Bugs* + +Archviles don't resurrect ghosts or glue resurrected enemies together + +Pain elementals don't spawn heads thru walls and across monster blockers + +Players do not get stuck falling off a ledge when restoring + +Monsters don't fall asleep after a restore + +Floor crushers work + +Trigger once functions don't vanish forever if they fail to activate at first + +Texture changers and donut functions don't crash the game if not set up right + +Using 1s doors or 0 tag triggers doesn't crash game + +Multiple tagged rising stairs work properly + +Tutti-frutti error has been eliminated -- short textures tile properly + +Medusa errors no longer slow the game to a crawl, and it is legal to have +multipatched textures on 2s normals + +If levels have no secrets, 100% secrets is displayed + +Par times removed if -file parameter is used, unless .deh modified + +Plats remember their heights across savegames correctly + +Sprites and flats load from the PWAD + +No crash on menu with large mouse sensitivity + +Screenshots can go past DOOM99.PCX and no longer display "Screenshot" + +Lower textures do not absorb bullets or rockets + +IDCLEV is disallowed when in the menus + +Crashes left staying in graphics mode are no longer possible + +Exclusive linedef functions like floor->highest neighbor floor and +doors can contain lines referenced to the tagged sector on both sides. + +Lights to max neighbor now works properly. + + +Command line parameters +------------------------ + +*Configuration Options + +-iwad + + If a full path is specified it must exist, and be a valid IWAD, and + becomes the one used. + + If a directory is specified, it is searched for each of DOOM2F.WAD, + DOOM2.WAD, PLUTONIA.WAD, TNT.WAD, DOOM.WAD, or DOOM1.WAD, in that order if + multiple IWADs exist there. + + If just an iwad name like DOOM2, DOOM.WAD, PLUTONIA, TNT.WAD, etc is + specified, or a custom iwad name is specified, then the current dir, + the BOOM exe's dir, the dir specfified by the environment variable + DOOMWADDIR, and the dir specified by the environment variable HOME + are searched in that order for a file by that name. The file located + must have an IWAD tag in the header or an error occurs. Game mode + (Shareware DOOM, Registered DOOM, Ultimate DOOM, or DOOM II) is + determined by the levels present in the file, and all appropriate to + the mode must be present for correct identification, though. A DOOM II + IWAD may be missing Map31 and Map32, to support the German version. + + If -iwad is not specified or an IWAD is not found there, the current + directory followed by the directory that BOOM.EXE was found in, then the + directory pointed to by the environment variable DOOMWADDIR, and finally + the directory pointed to by the environment variable HOME are searched, + for any of the standard IWADs. + + This option is new to BOOM, it was not available in DOOM. + +-save + + If exists, and is a directory, it becomes the path where save + games are stored, and loaded from. If -save is not specified or the + directory does not exist, then the current directory is used. The + parameter -cdrom overrides -save. The -save option is new to BOOM, it was + not available in DOOM. + +-cdrom + + When this parameter is specified, a directory named c:\doomdata is created + (if it doesn't exist) and is used to read and write both the configuration + file and savegames. It is not very useful with BOOM because BOOM does not + exist on any CDROMs, making this usually unnecessary. It can be used to + temporarily switch to a different configuration and savegame set however, + and is supported for that reason. Note demos are still recorded and played + back from the path you specify, and if none, that is the current directory, + even if you are running from a cdrom. + +-nosound + + This parameter disables both music and sound during play. + +-nomusic + + This parameter just disables music while playing. + +-nosfx + + This parameter only disables sound effects when playing. + +-config + + The -config parameter allows you to use a different configuration file than + BOOM.CFG for the DOOM session being started. If -cdrom is specified -config + will be ignored. Its primary use is to support different BOOM setups from a + single directory, when different people use the same installation and have + different preferences. If the configfile specified does not exist it will + be created with all default values. + +*Play Options + +-nomonsters + + This option allows you to eliminate any monsters from the level you are + playing, just to check it out first, or during deathmatch where monsters + may be a bother. Unlike DOOM -nomonsters will remain in effect after you + IDCLEV to another level. This switch will not affect demo playback or + savegames you load however, if they had monsters when recorded, they still + will. + +-respawn + + This option, only for the seriously deranged , causes monsters to come + back to life (i.e. respawn) 8 seconds after you kill them. + +-fast + + The -fast option gives you the nastier monsters of nightmare skill level + without the nasty habit of coming back to life 8 seconds after you kill + them. In combination with UV skill, this is another higher skill level, + sometimes called "grandmaster" skill. + +-turbo + + The -turbo option allows you to control the speed of your player. The + number nn should be in the range 0 to 255, with 100 the default player + speed (100%). Use higher values for a speedier player, and smaller values + for a slower player. Values higher than 100 are cheating when recording + demos or playing deathmatch. + +-record + + The -record option allows you to make a recording of your play during a + DOOM session. The recording will continue thru level exits, you must quit + the DOOM session to end the recording. The 'Q' key, which used to terminate + demo recording, all too often accidentally, has been disabled. Unlike DOOM + you may specify a path to the recording, and the .LMP extension may be + specified if desired, though it is not necessary. + +-maxdemo + + In DOOM the demo buffer was by default 128k, the demo recording session + would exit on exceeding this amount (a little over 15 mins), and you could + extend it to larger fixed sizes with this parameter, which represents the + number of kilobytes allowed for it. This is no longer necessary, the demo + buffer will grow as needed during demo recording, as memory permits. You + might still want to specify this parameter, if only to avoid any slight + pauses at inconvenient moments, but really they should not be noticeable + in any case. + + +*Loading Options + + *New: You may specify .wad, .deh, .bex and .lmp files without using + the -file, -deh and -playdemo switches, and they will be understood + and treated as if their respective switches had been specified. + This only works if those files appear on the command line before + any switches. For example, + boom fred wilma flint.bex pebbles.lmp + is the equivalent of + boom -file fred.wad wilma.wad -deh flint.bex -playdemo pebbles.lmp + Remember that this won't work: + boom -nomonsters fred wilma + but this will: + boom fred wilma -nomonsters + The first -switch on the command line ends the auto-identification + functionality. + +-file ... + + The -file parameter is followed by the names of the PWADs (DOOM levels + created by users rather than id) you want to load with the game. The .WAD + extension is optional (unlike DOOM). If any file is not found, BOOM will + exit, informing you of which was missing. You can specify paths as well as + filenames. The order you load PWADs can sometimes be important. Generally + speaking if two PWAD files both specify the same resource, the last one on + the line is used. Consult the PWAD's documentation for proper loading + order. + +-warp +-warp +-warp + + The -warp option allows you to "warp" directly to a level rather than going + thru the startup menus. In DOOM, two numbers are required, first the + episode number 1-4, then the mission number 1-9. In DOOM2 only a single + number is required, the Map or level number 1-32. + *New: if no level number is specified with -warp, BOOM will automatically + warp to the first level present in any pwads that have been loaded. + +-episode + + The -episode parameter is only useful with DOOM, not DOOM II, and it warps + you to the first level of the episode 1-4, that you specify. It is + equivalent to -warp 1. + +-skill + + The -skill parameter overrides the default_skill setting in BOOM.CFG if you + want to temporarily play at a different skill level. + +-deh + + The -deh parameter allows you to specify a DEHACKED patch to change default + engine behavior. You can specify a .DEH file, which is backward compatible + to DEHACKED v3.1 format (patch format 6) outside text changes, or a .BEX + file which allows BOOM extensions to DEHACKED. See DEH support in BOOM + below for more detail. Note the -deh option is new to BOOM, it did not + exist in DOOM. + +-playdemo + + The -playdemo option loads the demoname and starts it playing. An optional + path and .LMP extension can be specified. When the demo ends BOOM will + exit. If the demo was recorded with PWADs loaded, you need to load the same + PWADs during demo playback. Note that BOOM will play demos from any version + of DOOM or DOOM II starting with v1.2, but only demos recorded by + the current version of BOOM are guaranteed to work. BOOM will never exit + with the message "Demo recorded with wrong DOOM version" however. + +-timedemo + + The -timedemo option is like -playdemo except that the demo is played much + faster, and after the demo exits the video frame rate measured during demo + playback is output. + +-fastdemo + + The -fastdemo option is like -timedemo, except that it runs as fast as + possible. The -fastdemo option is new to BOOM -- it did not exist in DOOM. + +-loadgame + + The -loadgame option is used to load a savegame directly from the command + line, without using the menus or function keys. If the save was recorded + with PWADs loaded you need to load the same PWADs with -file when you use + -loadgame. If -loadgame is used you may not use -record or any of the demo + loading options at the same time. The loadgame parameter is 0-7, to load + the first thru eighth savegame slot respectively. Note DOOM only supported + 6 savegame slots. + +*Debug Options + +-devparm + + This option is used during development, or at least it was designed that + way. It does two things of interest. It causes dots to be displayed in the + lower left corner whose number is inversely proportional to frame rate. It + redefines the F1 key to take a screenshot instead of displaying the HELP + screen. + +-nodraw +-noblit + + The nodraw and noblit options are primarily used to isolate the video from + other processes when measuring BOOM's speed. They should be used with + caution, as nothing will display on the screen when they are used and + you'll need to exit BOOM "by feel" without the aid of menus. Press ESC, + Q, Enter to do this. The nodraw option suppresses all drawing, while the + noblit option simply suppresses the transfer of screen data from the + internal buffer to the screen. + +-dumplumps + + The dumplumps option causes the predefined lumps in the BOOM engine + to be written out as a .WAD. This is so wad authors can inspect + them, understand them, and replace them. BOOM exits after writing + the predefined lumps to the file. + +*Obsolete Options + +-shdev +-regdev +-comdev + + These three switches were used by id in their development to switch between + shareware, registered, and commercial versions. They do not work in any + other context and are not useful for end users. + +-wart + + In BOOM -wart is completely equivalent to -warp. In DOOM it was used to + support id's development and had many strange and not too useful properties + for the end user. + +-statcopy + + Not fully understood, has something to do with an external statistics + device whose interface must be linked into the program. Unless you have + one of those, suggest you leave it alone . + +----------------------------------------------- +Section 4. Playing BOOM in Serial/Network mode +----------------------------------------------- + +IPXBOOM and SERBOOM are provided with the BOOM distribution and provide +equivalent functions to IPXSETUP and SERSETUP except they recognize BOOM.EXE +by default. DM.EXE will also work if you change its DM.CFG file to point to +IPXBOOM.EXE or SERBOOM.EXE. + +*Multiplayer Options + +-deathmatch + + The -deathmatch parameter tells BOOM to start up in deathmatch mode as + opposed to cooperative mode when starting a multiplayer game. + +-altdeath + + The -altdeath parameter (must also use the -deathmatch parameter) specifies + that items other than invisibility and invulnerability will respawn after + being picked up. + +-timer + + The -timer parameter allows you to set a time limit on the length of a + deathmatch game. After nnn minutes have elapsed the level will end and the + frag scores will display. + +-frags + + The -frags option allows you to deathmatch until one player achieves nn + frags, at which time the level ends and scores are displayed. If is + not specified the match is to 10 frags. This option is new to BOOM and was + not available in DOOM. + +-avg + + The Austin Virtual Gaming mode is equivalent to -timer 20. Deathmatches + end after 20 minutes of play. + +-extratic + + This option sends an extra copy of the player movements across the network, + perhaps making play smoother by redundancy in favor of retransmission, + which is slower. + +-dup + + Like -extratic this option sends extra copies of the player movements + across the network, perhaps making play smoother by redundancy in favor of + retransmission, which is slower. This option allows you to specify the + number of extra packets sent however, from 1 to 9. If you specify less + you'll get 1, if you specify more you'll get 9. + +-debugfile + + When used outputs network debug information to debugN.txt where N is the + player number of the person who used -debugfile. + + +---------------------------- +Section 5. Editing for BOOM +---------------------------- + +Editors with full or partial support for BOOM +--------------------------------------------- + +DETH v4.14b (freeware) provides full BOOM support, and is available from the +same location you obtained BOOM: + +http://www.teamtnt.com. + +This program is in beta and should be used with caution. DETH runs under +DOS, Win'95's MS-DOS mode, or in a FULL SCREEN Win'95 Dos Window. + +DCK 2.2f (freeware) will support BOOM, and DOOM17.DAT is provided at the +TeamTNT site mentioned above. DCK 3.62 (shareware) will NOT work with +generalized linedef types, they are truncated to 255 on output of the +wad. Limited support is available thru the DOOM19.DAT file also provided at +the TeamTNT archive. DCK will only run under DOS, or Win'95s MS-DOS mode. + +DeeP '97 (shareware) provides full BOOM support. DeeP '97 runs in Win'95. +You may get DeeP '97 from ftp.cdrom.com or http://www.sbsoftware.com. + +WadAuthor (shareware) partially supports BOOM thru use of a custom BOOM.WCF +file, available at the site above for BOOM and DETH. We are working on +getting full support for BOOM in WadAuthor soon. + +DMapEdit has recently been improved to include some support for BOOM. It +basically supports direct numeric entry for the new features. The URL +for the beta version is: http://babtech.com/doom.html. + + +Wad extensions in BOOM phase I +------------------------------ + +BOOM has not changed the wad structure in any significant fashion, merely +made use of previously undefined bits and fields. Nearly any editor will +work, but not all will allow or support use of the advanced editing features +of BOOM. See BOOMREF.TXT in the EDTUTIL download for details. In brief, the +differences are: + +1) Linedef types filled out to values 0-269 +2) Generalized linedef types added in range 2F80H - 7FFFH +3) Generalized sector types using bits 5-11 of the sector type field +4) New things 5001, 5002 added to support point source wind effect +5) New thing flags, bit 6 "not in DM", and bit 7 "not in CO-OP" +6) New linedef flag, bit 9, PassThru, that allows one push to activate + several functions simultaneously. + +BOOM supports embedding MIDI directly in the WAD file instead of MUS thereby +eliminating length limitations and improving fidelity. + +BOOM recognizes two new lumps, SWITCHES, and ANIMATED. These allow the wad +designer to extend or replace the list of switches and animated textures and +flats recognized by the engine. A tool SWANTBLS.EXE is provided to turn a +simple text file into these lumps, along with the default text file +definition, DEFSWANI.DAT. + +BOOM also recognizes the lump TRANMAP so that authors can create their own +translucency lookup table for specialized effects. + +BOOM supports an arbitrary number of colormaps, defined between the C_START +and C_END markers, and predefines one called WATERMAP. Any of these colormaps, +as well as Doom's predefined COLORMAP, can be used with the 242 linedef to +change the lighting the player sees in normal space, below fake floors (water), +and above fake ceilings. + +BOOM supports replacing sprites and replacing and adding flats directly from +a PWAD. + + +Using CLED to supplement your editor +------------------------------------ + +If your editor will not support the features of BOOM, all may not be lost +if you are editing for DOOM II. If like DCK 3.x, it changes the fields it +doesn't understand, it won't be possible, but if like EdMap it ignores them, +the following will work. + +There is a command line DOOM II/BOOM editing tool called CLED, provided at +the TeamTNT site that can be used supplement your editor. It is used by +noting the number and kind of object you want to set a field for in your +regular editor, then after saving and exiting, using CLED to modify the +parameter your editor doesn't support. + +Example: Suppose in Map03 of MYWAD.WAD you want to set linedef #543 to type +20152. You would type: + +CLED MYWAD LINEDEF(3,543).TYPE=20152 + +CLED comes with documentation containing more examples. It can be used to +set any of the BOOM-extended fields mentioned above. + +TRIGCALC.EXE is also available and will output the generalized linedef type +number for a function you describe by answering questions. + + +DEH Support in BOOM +-------------------- + +The popular program DEHACKED.EXE has been used for some time now to change +things about the game that are hardcoded into the executable program. +Dehacked was created by reverse engineering what the internal structures +and behaviors are, and is a remarkable program for its intended purpose. + +Unfortunately, 100% compatibility with all dehacked files is impossible to +achieve due to the way dehacked works. In particular, text changes are done +in a manner that is dangerous and error prone. To make it easier to alter +text strings in DOOM (like the level names in the automap, intermissions, +etc.) a new format for text has been created for BOOM. The separate text +file BOOMDEH.TXT will go into considerably more detail about those items, +and shows what the current defaults are for all 300+ strings that are +changeable in BOOM. + +DEHACKED.EXE creates files with a .DEH extension, and those are supported +in BOOM, other than general text string changes. You also can use a new +extension, .BEX (Boom EXtended format), which can contain any of the regular +Dehacked items such as code pointers, frame wiring, etc. as well as the new +BOOM extended text format. Although you can put any valid DEH or BEX data +in a file with any extension (if you specify it on the command line, you can +use -deh myfile.zyx, if you like), the idea is that a .DEH file will contain +only items that Dehacked would understand (you can use the rather excellent +Dehacked interface to adjust those) and that a .BEX file would contain +string extensions as well. + +There is now an "include" directive available to allow a .BEX file to +include the contents of one or more other .DEH or .BEX files + +You can also change par times with .BEX files. + +You do need to use a Dehacked file from version 3.0 or 3.1 of DEHACKED.EXE, +which says in the .DEH file itself that it is a Patch format 6. We have +also had success with Patch format 5, though compatibility is not +guaranteed. + +** New: Code pointers may now be changed in a .BEX file by using mnemonic +(words) for the pointers, such as "Chase" and "Scream". Pointers may also +now be put into any frame, whether there was one there before or not, an +improvement over that limitation in Dehacked. Extensive information is +available in BOOMDEH.TXT for code pointers including a listing of the +original frame information for reference. + +See BOOMDEH.TXT for details. + +--------------------------------------------- +Section 6. Differences between BOOM and DOOM +--------------------------------------------- + +1) Savegames + +BOOM does not support compatibility with previous engine's saves at all. +Only savegames made by BOOM may be loaded in BOOM. If future versions of +BOOM are released usability of savegames will not be guaranteed. + +2) Demos + +BOOM supports demos made with previous engines in a limited fashion. +Some will go out of sync, though none will cause the engine to crash. This +is almost unavoidable due to the large number of changes made in the engine. +Internal demos may now contain revenants without losing sync. + +The demo_insurance option in BOOM.CFG allows one to make tradeoffs between +demo sync and game chaos. If demo_insurance=0, Boom demos stand a good chance +of losing sync on the next version of Boom, but the gameplay is more chaotic +because a single RNG is used for all events. If demo_insurance=1, Boom demos +stand a greater likelihood of staying in sync across Boom versions, but the +game is slightly less chaotic as a result. The two goals of demo sync across +different versions of the game, and chaos during the game, are somewhat +contradictory. If demo_insurance=2 (the default), then the special steps are +only taken when demos are being recorded -- normal gameplay is unaffected. + +demo_insurance does not affect the playback quality of Doom demos, which were +recorded without Boom's special sync-preserving measures. + +Even when demo_insurance is enabled, the games are fully randomized -- the only +difference is whether independent game events will use the same RNG and thus +interact, increasing the chance of demo desync if the slightest change occurs +in the game, or whether each game event will use its own independent RNG. + +3) Wads + +BOOM supports playing old wads fully. The only proviso is that those wads +must not contain serious errors, BOOM cannot play them if they do. Due to +the more sensitive nature of DPMI protected mode, BOOM is highly sensitive +to accessing arrays past limit and other illegal memory usages. The usual +result will be exit from BOOM with the message "Segment Violation". CWSDPMI +under DOS is more sensitive than Win'95s DPMI, so some wads that won't play +under DOS may still play under Win'95 in a DOS Window. On the positive side, +if you test your wads fully under DOS with CWSDPMI, they are very likely to +play without trouble after uploading. Its also very unlikely that your +system can be corrupted by BOOM. The config file, BOOM.CFG, contains a +variable named original_doom_compatibility that will insure that BOOM acts +as much like DOOM v1.9 as possible, even preserving some bugs in that +engine. + + +----------------------------------------- +Section 7. Files in the BOOM Distribution +----------------------------------------- + +BOOM executable archive BOOM201.ZIP + +BOOM.EXE..........Executable BOOM program +ASETUP.EXE........Allegro setup utility +BOOM.TXT..........This file +SNDDRVR.TXT.......Additional documentation on setting up sound/music in BOOM +BOOMDEH.TXT.......Description of BOOM's version of DEH +BOOMREF.TXT.......Reference for all extended wad features supported by BOOM +KEYCODE.EXE.......Tool to output keycodes for use in config file +COLHELP.BMP.......Chart of DOOM colors for use in config file +SERBOOM.EXE.......SERSETUP for BOOM +IPXBOOM.EXE.......IPXSETUP for BOOM +CSDPMI4B.ZIP......DJGPP v2 runtime support including docs and config util +CWSDPMI.EXE.......DJGPP v2 runtime support executable +GO32-V2.EXE.......DJGPP tool to provide info on DPMI setup +COPYING...........DJGPP copyright, DOOM source and binary copyright + +DETH for BOOM (optional) DETH415b.ZIP + +DETH.EXE..........v4.12 of the freeware editor DETH with full BOOM support +DETH.FNT..........Font used by DETH +DETH.INI..........Option and configuration file for DETH +COMMON.CFG........Options common to DOOM and DOOM II for DETH +DOOM2.CFG.........Options specific to DOOM II for DETH +DOOM1.CFG.........Options specific to DOOM for DETH +INSTALL.TXT.......Brief note on installation of DETH +KEYS.TXT..........Summary of key functions in DETH +TEXTURE.TXT.......Brief document on texture alignment +CHANGES.TXT.......DETH v4.12 release notes +CSDPMI4B.ZIP......DJGPP v2 runtime support including docs and config util +CWSDPMI.EXE.......DJGPP v2 runtime support executable +GO32-V2.EXE.......DJGPP tool to provide info on DPMI setup +COPYING...........DJGPP copyright and distribution license + +EDIT utilities for BOOM (optional) EDITUTIL.ZIP + +CLED.EXE..........Command line editor for extended fields in wads +CLED.TXT..........Documentation for CLED +TRIGCALC.EXE......Tool to output generalized linedef numbers by Q&A +MUSPUT.EXE........Tool to embed MIDI files in a wad +MUSPUT.TXT........Documentation for MUSPUT.EXE +BOOMREF.TXT.......Reference for all extended wad features supported by BOOM +CWSDPMI.EXE.......DJGPP v2 runtime support executable +SWANTBLS.EXE......Creates SWITCHED.LMP and ANIMATED.LMP from input text file +DEFSWANI.DAT......File for SWANTBLS.EXE containg default switches & animations +MAKTRLMP.EXE......Converts a TRANMAP.DAT file to a .LMP for wad inclusion +MAKTRLMP.TXT......Documentation for MAKTRLMP.EXE +CMAPTOOL.EXE......Tool for creating custom colormaps by editing a BMP +CMAPTOOL.TXT......Documentation for CMAPTOOL.EXE +DOOMCOLR.BMP......BMP containing all colors edited for use by CMAPTOOL.EXE +COLBMPS.ZIP.......Edited examples of DOOMCOLR.BMP +COLMAPS.ZIP.......Colormap lmps produced by CMAPTOOL from example BMPS +COLMAPS.WAD.......Add-on WAD containing example colormaps + +EDIT configuration for WadAuthor (optional) WACFG.ZIP +(thanks to Rick (Wildman) Clark and Jonathan (DfA) Campbell) +BOOM.WCF..........Extended linedef, thing types for WadAuthor + +EDIT configuration files for DCK (optional) DCKBOOM.ZIP + +DOOM17.DAT........Extended types configuration for DCK2.2f +DOOM19.DAT........Extended types configuration for DCK3.x +DCKBOOM.TXT.......Documentation on DCK configuration files +WATERMAP.WAD......File that allows DCK to insert WATERMAP texture + +DEMO wads + +DAWNING.ZIP.......BOOM demo wad by Gary Gosson +RAGE.ZIP..........BOOM demo wad by Paul Fleschute +BOOMEDIT.ZIP......BOOM editor's tutorial wad by Jim Flynn + +-------------------------------------- +Section 8. How to report bugs in BOOM +-------------------------------------- + +If an error message during loading is involved in the problem, you'll +want to report it precisely, as that will help us identify the +problem. In order to capture the initialization screen completely use +the following command line: + +REDIR -eo BOOM (normal boom command line parameters here) > errlog.txt + +After exiting BOOM, the errlog.txt file will contain the full +initialization script so you can report the error exactly. + +If you find a bug in BOOM you should visit + +http://www.teamtnt.com + +and follow the directions there for reporting it. There will be a bug report +form, and an e-mail link provided. Please examine the bug report form to see +what kind of information we are interested in, even if you report it by +e-mail. This information will help us duplicate the problem, which is an +important step in fixing it. + +Your help in making BOOM bug free is appreciated. + +---------------------------- +Section 9. Acknowledgements +---------------------------- + +We'd like to thank id Software for this incredible game, and especially John +Carmack for his release of the source and his encouragement of our efforts. + +Credit is due Chi Hoang who sped us on our way with his near instant port of +the Linux DOOM source to DOS. BOOM is based on v.20 of his port. + +Credit also to Steve Bacquet, whose QMUS2MID utility provided the insight we +needed to get a working MUS to MID converter running. + +Thanks to Shawn Hargreaves who wrote Allegro, and made this possible in far +less time. + +Thanks to all the folks who wrote our compiler, DJGPP v2, and who continue +to support free software. + +Thanks to the non-coding DoomSource project members who tested our early +attempts and made many suggestions to improve the product. + +Thanks to BOOM's BETA crew who beat on the engine mercilessly for long hours +and diligently filled our mailboxes with what they saw. + +Gaston (Mordeth) Lahaut Ky (Rez) Moffet Jan Van der Veken +Joachim (Jou) Otahal Len Pitre Doug Freeman +Chris (Case) Christenson Jonathan (DfA) Campbell Ingo Kirsch +Tom Robinson Rich Brennan Kurt Schulenberg +Justin Madigan Lorenzo Cricchio Peter Zahner + +Thanks to Rick (Wildman) Clark and Jonathan (DfA) Campbell for creating an +editor configuration file for WadAuthor. + +Thanks to Bruce (DOOMGuy) Benko for his work making DMapEdit work +with BOOM. + +And finally thanks to all the DOOMers still out there who have kept the +game alive so long, and who gave us many fine ideas thru the newsgroups +and mail. Keep on Doomin'! + diff --git a/doc/prboom-history.txt b/doc/prboom-history.txt new file mode 100644 index 0000000..9591d0e --- /dev/null +++ b/doc/prboom-history.txt @@ -0,0 +1,566 @@ +This file shows a history of changes between PrBoom versions since v2.1.0. +For historical reference, the complete LxDoom changelog follows it. + +Changes from v2.4.7 to v2.5.0 +- Limit the game to one CPU core on a multicore machine [prb+] +- Fix crash when out-of-range values are used in fixed point arithmetic [prb+] +- Allow the game to build without SDL_mixer, add --without-mixer option to + ./configure to force this. Music support will be disabled in this case +- Don't set the default game skill from the new game menu + Instead there is an option to set it explicitly in the General menu +- Fix the brightness of the player's weapon +- Fix linear filtering on flats in software mode +- Fix crash when an unknown sprite is used with a non-zero frame number +- Restore special case for trivial maps (bug #1837422) +- Fix screenshots in high colour screen modes - if libpng is available at + build time it will be used, otherwise a BMP will be saved +- Don't process mouse input in the menus [prb+] +- Always use Doom's main menu order to avoid bugs with Alien Vendetta [prb+] +- Remove line of junk graphics below status bar [prb+] +- Restore Boom friction and bobbing code [prb+] +- Fix crash by testing for null targets in mancubus fire code pointers +- Restore last known enemy check in Boom compatibility [prb+] +- Animated middle textures with zero index forced [prb+] +- Better handling of unrecognised demo formats [prb+] +- Fix for hanging decoration disappearing in Batman Doom MAP02 [prb+] +- Fix menu description: pain elementals are limited to 21 lost souls +- Manual page fixes from Debian +- Fix position of netgame player arrows on the automap in rotate mode +- Ignore chat key presses in multiplayer demo playback and -solo-net +- In deathmatch demo playback always draw player arrows on the automap +- In a multiplayer demo, don't reset view to console player on a new map +- Fix crash when MP3 music is being used and the player changes back to + a piece of music that's already been loaded before +- Avoid HOM effect on large maps such as epic.wad 5 [prb+] +- Fix sound origins on large levels [prb+] +- Handle demos with bad or missing headers [prb+] +- Fix the colour of player 4 (red) in multiplayer +- Play correct player pickup sounds in multiplayer demos +- Don't allow solids to pass through no-clipping objects in Doom [prb+] +- Restore Dehacked's ability to set the raisestate of a mobj [prb+] +- Handle demos with a missing end marker [prb+] +- Ignore switches that reference unknown textures instead of exiting +- Fix crash when resetting a menu to defaults [prb+] +- Fix crash when trying to play demos from Boom 2.00 [prb+] +- Fix crash in multiplayer demos when there are still sounds playing + on map changes (e.g. players revving chainsaws) [prb+] +- Fix mouse clicks on the intermission screen being ignored +- Don't eat screenshot key presses (see sf bug #1843280) +- Detect Hexen-format maps and refuse to play them, instead of crashing +- Fix crash when loading maps with missing player starts +- The backs of switches must be pressable in any demo recorded by + Boom 2.01, even those in Boom's "compatibility" mode [prb+] +- Force comp_doorstuck=1 in Boom 2.01 compatibility mode [prb+] +- comp_dropoff=1 was broken in MBF compatibility mode [prb+] +- Restore --disable-dogs but make sure it doesn't break Dehacked +- Fix desync if the user presses pause on the intermission screen [prb+] +- comp_666 fixed: either cyberdemon or spider can end E2M8 or E3M8; + killing a baron on E3M8 won't cause the level to end any more [prb+] +- Fix broken string matching in Dehacked [prb+] +- Passing --without-net to ./configure will compile the game without + network support; this may help if your platform lacks SDL_net +- Fix crash when reloading network savegames (bug #1590350) +- Fix bug in transparency rendering caused by doing it in two places +- Added high color rendering + +Changes from v2.4.6 to v2.4.7 +- Fixed comp_soul and comp_maskedanim options not actually being optional. +- Fixed searching for IWAD/prboom.wad (bugs #1585185, #1585600) +- Multiple sound crash fixes (bugs #1577495, #1586800) +- Fix for previously introduced HOM error +- Fix frame numbering problem in MBF dehacked patches (bug #1576151) + +Changes from v2.4.5 to v2.4.6 +- Mac OS X: Fixed music volume slider +- Implemented patch clipping. This fixes bug #1557501. +- Fixed update of compatibility options after use of TNTCOMP cheat +- Reenabled padding if short or missing reject lumps. + Patch #1570517 by RjY. +- Removed unaligned memory access in r_drawflush.inl. This should fix bus + errors on architectures where unaligned access is forbidden and should + give a slight speedup on other architectures. +- Stop right after the quit sound stops, instead of waiting three seconds +- Fixed sound origin for switches. This is compatibility optioned. + Patch #1533045 by RjY. +- Fixed "oof" sound when hitting ground while already dead + Patch #1532700 by RjY. +- Ported Eternitys fix to show the "ouch" face when severly hit +- Unified drawing functions, this speeds things up a bit and fixes most + artifacts on small numbers and fonts in high resolution modes. +- Mac OS X: Add resolution and video mode (OpenGL vs software) selection to + launcher +- Added rendering filters for software mode, they are configurable in a new + page in general settings +- Emulate some texture composition bugs +- Fix more common WAD bugs that can cause crashes +- Fixed random crashes caused by use of uninitialized memory +- Fix some demo incompatibilities caused by slime trail removal +- Fixed crashes with WADs which use newer gl nodes or don't have any nodes +- Automatically load gwa files with gl nodes +- Fixed integer overrun in automap on large levels (from PrBoom+) + +Changes from v2.4.4 to v2.4.5 +- fix crash when saving the game in levels with lots of monsters + (bug introduced in 2.4.4) +- -nodeh option to disable automatic loading of dehacked-in-wad lump +- Unified software and opengl engine into one binary +- Added video mode selection to menu +- fix demo desyncs on E1M5 on x86_64 systems +- Fullscreen setting will only take effect after game restart +- reduce red palette translation if the menu is up, so the menu can still be read +- screenshots now in PNG format on Linux/Unix in GL mode too +- Added experimental -checksum option for demo playback comparison +- Merged new internal patch (graphics) format from PrBoom 2.3 +- Mac OS X: Launcher now uses drawers instead of tabs +- Mac OS X: Fix some longstanding Wad chooser issues +- Mac OS X: Add a console to display text output from PrBoom + +Changes from v2.4.3 to v2.4.4 +- Don't fail when a texture name can't be looked up +- Increased several limits - Thanks to entryway and RjY + - Increased number of sidedef limit to 65534 + - Increased number of vertexes limit to 65535 + - Fixed crash when crossing sectors with very big height differences +- fix crash on E4M8 +- New command-line options for setting a window (-window) or fullscreen + (-nowindow) mode temporarily. +- The maximal supported resolution is increased from 1600x1200 to 2048x1536 +- GLBoom will use the closest supported resolution when running fullscreen +- The "RUN" key inverts the autorun state +- Live monsters are highlighted in a different colour on the iddt-automap +- Fixed OpenGL sky rendering in Requiem and Memento Mori +- The "Show coordinates of automap pointer" setting works now +- merged many cleanups and fixes from PrBoom 2.3 +- fix translucency map file handle leak +- fix consistency failures in netgames +- prevent crashes at 800x600 caused by rounding errors with naive clipping +- fixed slowdown at 1024x768 on some systems +- ability to play tasdoom demos directly +- -solo-net option is a shortcut for one-player network games +- emulate spechit overflows for dosdoom and tasdoom compatibility +- made several cleanups and fixes + +Changes from v2.4.2 to v2.4.3 +- Massive speed improvements in higher resolutions taken from Eternity. + Thanks to SoM and Quasar!!! +- fix bugs in gameplay occurring with gcc-4.1 +- Mac OS X: Add "Show Game Folder" to menus, for easy installation of new + game wads +- Mac OS X: Disable games in popup menu whose wads cannot be found +- fix compilation warnings +- tidy up configure script + +Changes from v2.4.1 to v2.4.2 +- Move gamma correction tables into prboom.wad +- Clean up light level calculations for walls & sprites +- CheckIWAD uses ANSI C streams for better portability and error handling +- Make screen wipe time independent of resolution +- Applied various small cleanups and fixes from PrBoom 2.3.1 +- Fix problems with dehacked substitution of long strings +- End of level sound crash fixed +- Mac OS X: Added simple launcher which allows to configure the most common + settings +- Mac OS X: Uses Quicktime for music now to fix crashes (adapted from Jaakko + Keränen's work in Doomsday) +- Windows: Converted project files to free Visual Studio 2005 Express Edition + +Changes from v2.4.0 to v2.4.1 +- PrBoom demos are now recorded with high-precision turning (like the + "Doom v1.91" hack that is floating around) +- when both -nodraw and -nosound are supplied, then no graphics will be + initialized and no windows opened +- add ultdoom compatibility level, and bring compatibility levels into line + with Prboom+ +- screenshots now use correct palette in software mode +- screenshots now in PNG format on Linux/Unix where available +- suppress use-supershotgun key in compatibility mode +- removed obsolete video related code +- fix screenshots on 64bit systems +- fix comp_666 + +Changes from v2.2.6 to v2.4.0 +- emulate reject overflows and spechit overflows - from prboom-plus +- more original doom compatibility options +- improve stretched graphics drawing for hires +- fix super-shotgun reload on last shot +- fix compilation with gcc 4.x +- fix some more dehacked support problems (e.g. Hacx) +- fix crash if pwad contains zero-length sound lumps +- added possibility to use mmap for wad access, this leads to less memory usage +- simplified the memory handling +- removed old Doom v1.2 lumps from prboom.wad +- windows also uses prboom.wad now +- add Mac OS X bundle build +- removed lumps and tables which are in prboom.wad from source + +Changes from v2.2.5 to v2.2.6 +- fix Inferno intermission screen crash +- fix lockup for other netgame clients when one client quits +- fix memory leak in netgame server +- fix SDL_LockScreen crashes on Win32 +- fix fuzz drawing for hi-res +- network games should survive temporary loss of connection +- fix dehacked NOSECTOR/NOBLOCKMAP effects +- fix player spawn sound + +Changes from v2.2.4 to v2.2.5 +- fix crash caused by long messages in HUD +- live monster counter on HUD +- notify server if client quits during startup wait +- improved response file parser +- fast forward to given map # in demo playback +- fixes for various sound bugs +- fix doom2 demos at levels with >10 deathmatch starts +- and more compatibility and demo fixes +- support higher-turning-resolution demos from v1.91 +- fix compilation with gcc 3.4.x + +Changes from v2.2.3 to v2.2.4 +- fixed sky-over-sky HOM +- add sound compatibility option +- improve sound volume accuracy +- shared texture palette isn't the default anymore +- better invulnerabilty rendering for non paletted OpenGL +- network game server can now read config files to set game options +- fix latency problems in LAN games +- small compilation fixed for OpenGL on some unix platforms +- fix for dehacked files which change frames +- fixed name clash when compiling for some unix platforms +- flag counted items with different colour on the IDDT automap +- fixed extra shot sound when chaingun runs out of ammo +- fix some telefragging related desyncs +- fixed offsets for flipped sprites +- hopefully fix problems with network games on big-endian platforms + +Changes from v2.2.2 to v2.2.3 +- improved mouse handling +- intermission demo sync bug fixed +- framebuffer update fixes (solves flicker on fbcon) +- -forceoldbsp allowed in non-GL version, and saved in demos +- fix player colours in multiplayer demos +- apply workarounds for buggy pwads even during demo playback +- fix numpad 5 key +- allow compilation on systems where SDL is built without joystick support +- fix comp_skymap +- using anisotropic filtering when the OpenGL extension is available +- using paletted textures when the OpenGL extension is available +- added gl_use_paletted_texture option to glboom configuration file +- using shared texture palette when the OpenGL extension is available +- added gl_use_shared_texture_palette option to glboom configuration file + +Changes from v2.2.1 to v2.2.2 +- more demo sync problems for original Doom and Boom fixed +- added changeable samplerate for soundmixing +- added fullscreen/window toggle in option menu +- added double buffering +- floor rendering made more accurate +- Win32 config file handling fixed +- fix endian conversion problem on Linux/PPC + +Changes from v2.2.0 to v2.2.1 +- improved fix for demo sync problems with lost souls bouncing off floors +- fixed bug where loading a -fast or -respawn savegame failed to restore + those options properly +- fixed demo sync bug with doors also tagged to lift triggers +- fix some endianness problems in the OpenGL renderer +- hopefully fixed some problems compiling for Linux/ARM +- fix multi-level demo time totals to agree with compet-n +- linux rpm is now a bit more standardised + +Changes from v2.1.2 to v2.2.0 +- fix compiling problem on alpha processors (size_t != unsigned long) +- fixed stair building (ex. TNT - Evilution MAP30) +- fixed OpenGL menu drawing bug +- hopefully fixed top sky line bug for some OpenGL drivers +- added joystick support through SDL +- made a (temporary) fix for the crash at 800x600 when timidity can't find cfg +- fixed some key binding problems +- fixed linking problems on some UNIX systems + +Changes from v2.1.1 to v2.1.2 +- fix problem with sound stereo +- fix problem with new network games +- supports demo files with base name >8 characters +- enable IDDT and other display cheats in demo playback +- various fixes for running on Solaris/sparc + +Changes from v2.1.0 to v2.1.1 +- config file is now prboom.cfg for the non-GL version, glboom.cfg + for the GL version. If you have used PrBoom (or LxDoom) before, + rename your old config file (boom.cfg) appropriately. +- fullscreen is now default for new prboom.cfg +- included sdl_mixer.dll now plays midi-files +- if waveout is used for sound (Windows NT4) the sound doesn't stutter anymore +- redid parts of the OpenGL renderer + - sprites behind translucent walls are rendered correctly + - translucent walls are rendered correctly + - support for glBSP nodes + - compliant to glBSP spec v2 + - use_mipmapping option in boom.cfg + - the default for zone memory in OpenGL is now 16MB +- fix screen melt transition +- most Boom demos should now work +- a lot more original Doom demos work +- keycard switches are shown coloured on the map, like doors +- improved ENDOOM rendering +- non-highres rendering functions dropped + +----------------------------------------------------------------------------- +This file is a log of all the changes to LxDoom since v1.0.0. +Note that LxDoom v0.* was a seperate line of development. + +* Changes from v1.4.4 to the PrBoom merger +- Fix rare demo sync problem (LxDoom v1.4.x bug, only noticed on Memento + Mori DEMO3) +- Fix memory management bugs + - Memory wasted by bug in Z_FreeTags (original Doom bug) + - Store correct size in extra memory blocks (Boom/MBF bug, harmless + except when debugging) + - Fix level precaching + - Disabled by default, controlled by config file + - Fix needlessly locked lumps (bug since LxDoom v1.3.2) + +* Changes v1.4.3 to v1.4.4 +- Install documentation in the right directory +- Sound code cleanup +- Fix problem with network games often desyncing immediately at startup + +* Changes v1.4.2 to v1.4.3 +- Improved mouse resolution, thanks to a patch from Barry Mead +- Various robustness fixed to the networking code +- Fixed various build problems + - gcc 2.7.2 compile problems fixed + - uid_t problems on odd systems should be fixed + +* Changes v1.4.1 to v1.4.2 +- Fixed various build problems, including + - Networking not work on many systems + - Portability improvements, in particular for Sparc + - Autoconf getting confused on systems with X headers in the include path +- Fix bug with music not looping after being unpaused +- Misc minor improvements + +* Changes v1.4.0 to v1.4.1 +- Fixed occasional tutti fruiti on non-power-of-2 short textures +- Fixed minor bug in the WAD autoloading code +- Fixed the function keys in lsdoom, thanks to a patch from Chris Purnell +- Fixed all compile warnings with the new gcc + +* Changes v1.3.7 to v1.4.0 +- License is now the GNU General Public License, see COPYING. +- autoconf based setup makes compiling LxDoom simple on most systems; + automatically compiles only the versions and features your system + supports. +- Fixed rendering bugs: + - tall vertical shafts did not block vision on the automap, and on x86 + systems there could be crashes in the column rendering near such shafts ( + bug from original Doom). + - fixed slowdown caused by non-power of 2 height textures in the case + where they don't tile (i.e. almost all cases) (this was the tutti-fruiti + "fix" from Boom, I've "fixed the fix" in a sense) + - fixed a slight slowdown caused by a bad optimisation (an old LxDoom bug) +- Fixed bug where things were rendered brighter in high-res, depending on the + resolution (bug from PRBoom). +- Improvements to the sound code: + - Fixed noise at the start of sounds playing (thanks to Steve Van Devender). + - LxDoom detects the music or sound server exiting (alright, so I mean + crashing ;-) and stops sending more data. +- Performance improvements, including: + - New algorithm in P_GroupLines, saves seconds when loading big levels. +- Fixed problems with the networking code, where multihomed machines could + get the wrong IP registered with the server. +- Screen code improvements: + - Rewrote screen layout logic, fixing numerous bugs. + - Fixed bug with flipped patches in high-res that could cause crashes, + thanks to Gady Kozma. + - Independent x and y scaling of the status bar, by Gady Kozma. +- Added warning messages whenever LxDoom auto-corrects errors in buggy + PWADs. Also made -devparm cause LxDoom to initialise all textures at + startup, so all texture errors are found at once. +- Store config files and save games in ~/.lxdoom/ instead of the current + directory, removed the -cdrom parameter. +- Fixed Doom bug where the "got a medikit you REALLY needed" message was + never used. Thanks to James "Quasar" Haley for pointing that one out. +- Made the numeric keypad keys be treated differently from their normal + equivalents, so you can bind them to different actions. +- Fixed bad importing of mobj pointer reference code from MBF. +- The level completed screen is now shown after ExM8 levels in Doom 1/ + Ultimate Doom. +- Minor coop improvements: + - Total game time shown on the intermission screen, as for single player + - Quicksave enabled +- Removed the frame rate dots, instead I added a cheat code "IDRATE" to show + various rendering stats, including the frame rate. +- Fixed the intermission screen code to store its data right (bad code from + original Doom). + +* Changes v1.3.6 to v1.3.7 +- Client-server style net-games, including new server program +- LxDoom starts faster, thanks to an idea borrowed from DosDoom +- Player colours system sorted out, now your personal player colour is part of + your player preferences. +- Fix for problems with 24bpp screens, new option in config file to deal with + this. +- Fix key setup problems where certain choices of key setup could hinder + message typing in multi-player. +- Misc stuff + +* Changes v1.3.5 to v1.3.6 +- Hires for the SVGALib version +- Automap rotation/overlay +- Modified to work with the new musserver +- Various misc improvements + +* Changes v1.3.4 to v1.3.5 +- Fixed nasty overflow in I_GetTime_RealTime, causing hangs +- Removed a load of I_GetTime references in m_menu.c +- Added support for music pausing/unpausing + +* Changes from v1.3.3 to v1.3.4 +- More MBF features/improvements imported: + - Internal improvements (mobj pointer reference counting) + - Enhanced skies support +- Status bar scaling for high-res +- Bug fixes: + - Occasional corrupt save-games in large levels fixed (Boom/MBF bug) + - -loadgame crashes fixed +- Performance improvements +- Command line argument parsing logic changed for convenience in shell scripts + (later arguments take precedence) + +* Changes from v1.3.2 to v1.3.3 +- Optimised i386 assembly some more, about 2% improvement in fps +- Tested to compile and run on FreeBSD +- Modified #includes to use current headers +- Updated makefile hints for FreeBSD compiling +- Made install script more portable + +* Changes from v1.3.1 to v1.3.2 +- Imported/added some MBF features + - New code pointers added + - "Faster" sprite sorting + - Improved Dehacked handling (more reliable, Dehacked-in-a-PWAD) + - Fractional floor attributes saved in save-games + - Auto-correction of common errors in wads +- Massive internal improvements, making LxDoom more stable - WAD lump locking, + rewritten patch drawing code. +- Improved config file handling - now accepts (and writes out selected) numbers + in hex, entries are sorted into sections with headers, and internally the + handling is better. +- Portability improvements - LxDoom is now near-completely endian-corrected, so it + should be compilable on big-endian machines, read the little endian Doom data + files fine, and even network with other machines regardless of endianness. + Also lots of misc portability stuff, explicitly signing some variables, a + lot more stuff made const, etc. The only problem I think is that save-games aren't + yet interchangeable across endiannesses. +- More memory efficient - block memory allocator reduces memory fragmentation, + video buffers allocated only when needed, more things made constant and + initialised better. +- Imported bug fixes from MBF: + - File handle leak in translucency code + - Water sector sprite problems + - No chat in demo playback + - Archville fire spawn + - Scroller calculations overflow + - Fast shots going through walls + - Improved d_deh.c fixed numerous SIGSEGV's and error code bugs + - Zombie players exiting levels + - New thing flags caused incompatibility with buggy Doom wads + - Setup menu backspace + - Indigo/Brown default chat keys reversed +- Glide (3dfx) frame-buffer target (warning - released only in source code form and + only for alpha testing purposes, not ready for normal use). +- Improved ENDOOM support - (optional) colour ENDOOM display, non-ASCII + characters displayed, sensible choice between ENDOOM and ENDBOOM (displays + any from a PWAD in preference, randomly chooses otherwise). +- Minor improvements so LxDoom integrates more naturally into UNIX systems; sound + and music server are now searched for via the path, and wads are looked for in + /usr/local/games/wads/ if no DOOMWADDIR is set. +- More keys work in the SVGALib version (notably PAUSE). +- Monster-monster kills in coop are assigned to the player the monster was + targeting, or a random player if it wasn't targeting any, so coop + monster kills stats total 100% at the end-level screen. +- Total game time is displayed on the intermission screens. This is a simple + total of the times shown on the end-level screens so far this game, not + including intermission times, and accurate to 1/35 of a second. +- Low sound volume fixed. +- Multi-player colours selectable in the config file. +- Misc minor fixes and improvements inspired by MBF: + - Support for -noload parameter + - Support auto-loading of deh/BEX files as well as wads + - Removed limit on number of wad-files loaded. + - Fixed buffer overrun in menu text writing code by wrapping long lines. + +* Changes from v1.3.0 to v1.3.1 +- Fixed saving of config file (bug affected most Linux systems) +- Binaries are now use libc.so.6. +- 24 bpp and 32 bpp true colour X displays are now supported (untested) (24 + bpp only supported for i386 systems). +- Auto-loading of wad files - in the config file there is a new option + which lists wad files to be loaded automatically (several directories are + searched as for IWADs). +- Loading saved games during a multi-player game now works. Very handy for + coop games :). +- New config file option controls music pausing - when the game pauses + the music can either continue or be stopped. So people playing at home can + have the music continue while they read the map; people playing at work + can pause when the boss comes in and have the music stop ;). +- Several more variables added to config file, most notably the + default size of the LxDoom window (for high-res). +- PRBoom v2.02 networking code is now used. It still doesn't network with + PRBoom though. Net games with just LxDoom work fine still. Anyone with ideas + why it won't go with PRBoom let me know. +- If saving a demo/screen-shot/save-game to disk fails an error message is + displayed. One of the most frustrating features of all versions of Doom that I + have used is that they always say "Game Saved" even if you are + out of disk space and it didn't save. I'm glad to have this one fixed. +- Fixed the -cdrom parameter. +- Fixed makefile hints for FreeBSD. +- Fixed music pausing causing musserver crash. +- Documentation updates. + +* Changes from v1.2.0 to v1.3.0 +- Hi-res added to X-windows version +- Portability improvements (FreeBSD and RISC stuff in the makefile, minor code + changes included) +- Minor bug-fixes + +* Changes from v1.1.1 to v1.2.0 +- Boom v2.02 updates incorporated (see TeamTNT's site for info on that) +- Improved music comms code, to pass instruments and volume info +- Code reorganisation & tweaking; video code is more logically organised, + and SVGALib code is neater now. + +* Changes from v1.1.0 to v1.1.1 +- Fixed crash using -warp parameter with SVGALib version +- Fixed music server communication code +- Should compile using glibc + +* Changes from v1.0.1 to v1.1.0 +- SVGALib version +- Fixed timing problem on buggy kernels causing crashes in the wipe screens +- Fixed bug in sound server communications which prevented Doom 1 working +- Fixed problem with sound code causing accelerated sound on v2.1.125 kernel +- Removed need for IPC in sound server communications, used pipe instead +- Improved mouse grabbing/ungrabbing code in XFree86 version, now depends on + game and window status +- Improved TrueColor/DirectColor 16 bpp support +- X version is more multitasking friendly - detects when it is hidden or paused + or an intermission screen is up, and tries to free some more CPU time. + +* Changes from v1.0.0 to v1.0.1: +- 16 bpp colour modes now supported. If you use a 16 bpp colour mode (65 + thousand colours approx.), then you don't have to change your X setup to 256 + colours (8 bpp) before using lxdoom anymore. However, it is still a good idea + to use 256 colours, because it is faster that way. +- fixed a minor bug in routine used for the 8 bpp '-2' option (screen + doubling), which caused a couple of lines to be missed at the bottom of the + display window. +- fixed a Boom bug which caused crashes in multi-player games. The bug occurred + when, during a single game session, first the players played one level at + which someone died, and then later exited that level, and later still another + player died. I.e in a multilevel death-match, or a long coop game. Caused one + machine to exit lxdoom with 'Segmentation Violation'. The version of PRBoom I + have also exhibits these symptoms, though obviously I can't be sure that this + is the cause; Boom v2.0 and v2.01 had this bug. + diff --git a/doc/prboom-plus-game-server.6 b/doc/prboom-plus-game-server.6 new file mode 100644 index 0000000..03a06ad --- /dev/null +++ b/doc/prboom-plus-game-server.6 @@ -0,0 +1,118 @@ +.TH prboom-plus-game-server 6 "2011-06-27" +.SH NAME +prboom-plus-game-server \- Server for network games of PrBoom+. +.SH SYNOPSIS +.B prboom-plus-game-server +[\| \-adfnrv \|] [\| \-e \fIepis\fR \|] [\| \-l \fIlevel\fR \|] [\| \-t \fIticdup\fR \|] +.BR +[\| \-x \fIxtics\fR \|] [\| \-p \fIport\fR \|] [\| \-s \fIskill\fR \|] [\| \-N \fIplayers\fR \|] +.BR +[\| \-c \fIconffilename\fR \|] +.BR +[\| \-w \fIwadname\fR[\|,\fIdl_url\fR \|]\|] +.SH DESCRIPTION +.PP +.B PrBoom +is a version of the first-person shooter game Doom, originally released by iD software in 1993. +.PP +.B PrBoom+ +is a Doom source port developed from the original PrBoom project. +It includes, amongst other things, the ability to play with several players +connected by a TCP/IP network. +.PP +To start a network game (often abbreviated to `netgame'), first the server +is started. \fBprboom-plus-game-server\fP accepts various parameters to control +the type of game (the skill level, number of players, level to play, optional +WAD file(s) to load, etc). +.PP +Then each player that wishes to participate runs +.B prboom-plus \-net +.IR hostname , +where \fIhostname\fR is the name of the machine on which the server is +running. Each copy of PrBoom+ retrieves information about the game from +the server, and when the specified number of players have joined, the game +begins. + +.SH OPTIONS +.TP +.BI \-N\ \fIplayers\fR +Specifies the number of players in the game (default \fB2\fP). The server +will wait for this many players to join before starting the game. +.TP +.BI \-e\ \fIepis\fR +The episode to play (default \fB1\fP). Unless you are playing Doom 1 or The +Ultimate Doom, and wish to play one of the later episodes, you do not need +to change this. +.TP +.BI \-l\ \fIlevel\fR +The level to play (default \fB1\fP). +.TP +.BI \-s\ \fIskill\fR +Specify the skill level to play (\fB1-5\fP). +.TP +.BI \-d +Set game mode to (old) deathmatch (default is cooperative). This is the original +deathmatch mode where ammunition and power-ups do not respawn and weapons remain +but can be picked up only once per player life, like in cooperative games. +.TP +.BI \-a +Set game mode to `altdeath' (called deathmatch v2.0 in the documentation) +(default is cooperative) where most items picked up by players respawn after a +while after being picked up. This also applies to weapons, which (unlike in +normal deathmatch mode) also disappear for a while upon being pick up. +.TP +.BI \-f +Select fast mode (monsters move faster). +.TP +.BI \-n +Selects nomonsters mode, i.e. there are no monsters in the game. +.TP +.BI \-r +Respawn mode. If you don't know what this is, you don't want to ;-). +.TP +.BI \-c\ conffilename +Specifies a configuration file to read which sets parameters for the +game. This is in the same format as the PrBoom+ configuration file (in +fact, you can ask it to read your normal PrBoom+ configuration file if +you want). Only certain settings are acknowledged: default_skill, +default_compatibility_level, the compatibility options and some of the +game settings (use \-v to have the server print the options as it +recognises them). +.TP +\fB\-w\fP \fIwadname\fR[,\fIdl_url\fR] +Specifies a WAD file to play. This is added to the internal list that the +server keeps. When a client connects, the server sends the list of WADs; +PrBoom+ will then add this to the list of WADs specified on its command +line. +Optionally, an url to the file can be given too; if when PrBoom+ connects +it cannot find the named WAD, it will attempt to retrieve the file +from the given url, extracting it if necessary. +.TP +.BI \-t\ ticdup +Reserved. +.TP +.BI \-x\ xtics +This causes extra information to be sent with each network packet; this +will help on networks with high packet loss, but will use more bandwidth. +.TP +.BI \-p\ port +Tells +.B prboom\-plus\-server +what port number to communicate via (default \fB5030\fP). +Note that if you change this from the default, then all the clients will +also need to specify this number when they try to connect (the default +programmed into PrBoom+ is also \fB5030\fP). +.TP +.B \-v +Increases verbosity level; causes more diagnostics to be printed, the more +times \fB\-v\fP is specified. +.SH SEE ALSO +.BR prboom-plus (6), +.BR prboom-plus.cfg (5) +.PP +For more information, see the \fBREADME\fP that came with PrBoom+. +.PP +Doom is a registered trademark of id software (http://www.idsoftware.com/). +.SH AUTHORS +See the file \fBAUTHORS\fP included with the PrBoom+ distribution. +This man page was written by Colin Phipps (cph@moria.org.uk). diff --git a/doc/prboom-plus-history.html b/doc/prboom-plus-history.html new file mode 100644 index 0000000..30233b7 --- /dev/null +++ b/doc/prboom-plus-history.html @@ -0,0 +1,1026 @@ + + + +PrBoom-plus: Change Log + + + + + +

Change Log

+ +
2.5.1.4 + @ 2016-Jan-10 + +
[+]Added "Fix clipping problems in large levels" option. +
[+]Added "gl_finish" config variable. +
[+]Added "mus_fluidsynth_gain" and "mus_opl_gain cfg" config variables to fine tune output of fluidsynth and opl2 midi. Values allowed are 0 to 1000. 50 (default) exactly matches old behavior. +
[+]Added a "Health Bar Above Monsters" option (health_bar* config variables). +
[+]Added a "Things appearance" automap option. Possible values: "classic", "scaled" and "icons". +
[+]Added "notarget" and "fly" cheat codes. +
[+]Added MBF's "-beta" codepointers. +
[+]Added a new HUD. +
[+]Added "shaders" sector light mode. +
[+]Support "Classic Doom" WAD files of Doom 3 BFG Edition, by Fabian Greffrath. +
[+]Support for HACX 1.2 IWAD, by Fabian Greffrath. +
[+]Support up to eight joystick buttons instead of just four, by Fabian Greffrath. The fifth and sixth buttons are mapped to strafe left and right. +
[+]Mouse look now is available in software mode. +
[+]Added a crosshair. Three different crosshair graphics are for choice: cross, angle and dot. Extra features are changing crosshair colors according to the player's health and/or on sight of a target. +
[+]Added "Allow Jump" option on "Prboom-plus 'bad' compatibility settings" page. Implemented by Fabian Greffrath. +
[+]Added a "Backpack Changes Thresholds" option. +
[+]-skipsec accepts a minutes prefix followed by a colon. +
[+]Two-key strafe50: StrafeOn + MoveLR = strafe50 +
[+]Added "Allow Vertical Aiming" option on "Prboom-plus 'bad' compatibility settings" page. +
[*]Update to newest SDL libraries: SDL_mixer 1.2.12, SDL_image 1.2.12, SDL_net 1.2.8. +
[*]Brown color for weapons that cannot be fired on weapon HUD widget. +
[*]"Use GL surface for software mode" mode now works much faster if gl_finish is 0 in config. +
[*]process_affinity_mask config variable is removed. Single CPU core will be automatically forced if SDL MIDI player is used. +
[*]Better support for Chex Quest, by Fabian Greffrath. Embed chex.deh by fraggle in prboom-plus.wad. +
[*]Redo MBF-style multiple DEHACKED lumps. Load command line DeHackEd patches after DEHACKED lumps. +
[*]Improved rendering precision (wall wiggle), by kb1. +
[*]Realign automap grid with blockmap. +
[-]'Max Health', 'Max Soulsphere' and 'Megasphere Health' DEH parameters did not work after warping to level from command line since 2.4.8.1. +
[-]Buggy invulnerability effect if hi-res textures are used and there is no support for GL_ARB_framebuffer_object. +
[-]Simple shadows flicker during lowering lifts. +
[-]"Change palette on pain/bonus/power" settings did not work. +
[-]Lines of walls on automap were not displayed on Planisphere 2 at some zoom. +
[-]Fixed HOMs on Planisphere 2. +
[-]Incorrect clipping of automap in opaque mode after changing view window size. Affects only software mode. +
[-]Fixed long wall error. +
[-]Boom's ability to change floor and ceiling lighting independently was not applied to things in opengl. +
+ +
2.5.1.3 + @ 2011-Dec-05 + +
[+]Added process_priority config variable. 0 - normal (default); 1 - high; 2 - realtime. +
[*]"Screen multiple factor" can be changed on the fly. +
[-]Fixed desynch on ep3-2349.lmp. +
[-]Fixed issues in screenshot and video capturing code. +
+ +
2.5.1.2 + @ 2011-Nov-25 + +
[+]Added device selection to portmidi player. Controlled by the snd_mididev config variable. See stdout.txt for list of available devices. +
[+]Added a progress bar for demo skipping during re-recording. +
[+]Added key binding options for start/stop and fast-forward when watching demos. +
[+]Added a key binding option to restart the current map. +
[+]Added a "Default compatibility level" GUI entry. +
[+]Support for 16 sprite rotations. http://zdoom.org/wiki/Sprite#Angles +
[+]New HUDs. HUDs definitions are moved to the "prboom-plus.wad/-prbhud-" lump. +
[*]Fluidsynth player now resumes notes seamlessly after a pause. +
[*]Speed improvement on maps like sunder.wad map10 and nuts.wad. +
[*]Force GL_LINEAR for MAG filter for textures with detail. +
[-]Fixed buggy music that forgets to terminate notes held over a loop point. SDL_mixer does this as well. Fix tested against Doom2 map14 and FreeDoom2 map01, for fluidsynth, portmidi, and OPL players. +
[-]Fixed buffer overrun in OPL2 player. +
[-]Fixed crash in video capture code when "Use GL surface for software mode" is enabled. +
[-]The screen wipe after pressing the exit switch on a level was noticably jerkier. +
[-]MBF-added codepointers worked with any complevel. +
[-]Fixed HOMs on skies with transparent pixels. Sky2 @ doom2.wad, for example. +
[-]Alpha channel did not work for 8-bit PNGs with alpha. +
[-]Transferred standard sky was drawn badly when FOV > 90 (Voodoo Guns - map02). +
+ +
2.5.1.1 + @ 2011-Jun-18 + +
[+]Support for ZDoom-style OGG loop points, by Nicholai Main. http://zdoom.org/wiki/Audio_loop +
[+]Ability to disable the background on Boom fullscreen menus. There is a "menu_background" config variable and in-game GUI entry. +
[*]"Automap - Show kills/items/secrets statistics" is displayed on overlay automap too. +
[*]Improved sample rate conversion, by Nicholai Main. +
[*]MIDI volume works independently from SFX on Vista/7 for "portmidi" player. +
[*]"madplayer" should now correctly play mp3 songs with ID3v2 tags. +
[*]Seamless tiling in the high quality resize algorithms (thanks Nunya). +
[*]The first frame of the O_COUNTD(.MP3) @ dvii-1u.wad is in fact corrupt, but the rest is ok (best guess: it used to have an ID3v2 tag which was removed by a dodgy program). The new load routine should be more accepting of damaged mp3s. +
[-]"Texture format" always forced to GL_RGB5_A1 if "Use GL surface for software mode" is enabled. +
[-]Fixed crash when you try to make a screenshot if "Use GL surface for software mode" is enabled. +
[-]Deadlock in "portmidi" music player. +
[-]Looping for all mp3s are completely broken in libmad player. +
[-]Fixed playback of mono MP3 files with libmad player. +
+ +
2.5.1.0 + @ 2011-May-28 + +
[+]New music code by Nicholai Main. See "Preferred MIDI player" GUI option. Available values are "sdl", "fluidsynth" (disabled for Win9x), "opl2" and "portmidi". +
[+]If "-warp" doesn't have a map number after it, the game will automatically warp to the first map in all the files loaded at the command line. This allows a pwad to be run without concerns about where its actual maps start. +
[+]The option to place the overlaid automap where you want (map_overlay_pos_x/y/width/height config variables). +
[+]Added render_patches_scalex/y config variables for custom scaling when "not adjusted" is used. +
[+]Emulation of weaponinfo overruns. +
[+]Automap interpolation. +
[+]Support for mouse wheel up/down. +
[+]Added an "Use mouse wheel for zooming" automap option. +
[+]Screen resolution changes don't require an engine restart. +
[+]The option to resize the main window with the mouse. Any size is allowed if Shift is pressed during resizing. +
[+]Alt-Enter now toggles between fullscreen and windowed modes. +
[+]Added an "Use GL surface for software mode" video option. It renders a software surface into an OpenGL surface. Thus, you shouldn't have palette issues on modern systems and you can use VSync even with the "windib" SDL video driver. +
[+]New video capture system by Nicholai Main. Use command line params "-timedemo anydemo.lmp -viddump filename.mkv"; see usage.txt for more details. +
[*]Added mouse button actions for "backward motion" and for single-click "use". +
[*]The noclip effect which can occur with an "intercepts overflow" should not take effect after a level is reloaded. +
[*]When recording, the ENDOOM screen is now disabled regardless of the misc_fastexit option. +
[*]The "Fast Exit" option is now named "Show ENDOOM screen". +
[*]The precise rendering of automap lines when "render quality" is "quality" to avoid small vibrations during map rotation. Applied only in GL mode. +
[*]Antialiasing of automap lines now also works in software modes (Xiaolin Wu's line algorithm). There's a map_use_multisamling config variable and a corresponding GUI entry for this. +
[-]Fixed a crash on map21 @ newgothic.wad with "-complevel 10" and higher. +
[-]Improved emulation of "missed back side" overruns. The desynch in fez1-924.lmp @ fez1.wad is gone now, but you still need to add "-setmem dosbox" or "-setmem dos71" command line parameter, because the default "dos622" memory layout causes a desynch. +
[-]The automap scaling factor is now saved. +
[-]A "-set overrun_* = 0" setting now works in demo footers. +
[-]When playing back multi-level demos for the Ultimate DOOM, -warp produced unexpected results when combined with -auto. +
[-]The titlepic in malgnant.wad caused Signal 11 crashes. +
[-]Fixed the incorrect positioning of automap marks when 'follow mode' and 'rotate mode' were enabled. +
[-]Automap marks did not draw correctly, displaying white rectangles instead of digits. +
[-]Automap marks did not scale to the selected screen resolution and were hardly visible at high resolutions as a result. +
[-]The skydome cap color was wrong during invulnerability mode. +
[-]The mouse cursor didn't hide during fullscreen when the mouse was disabled in General options. +
[-]When one used skipsec to warp to an intermission screen portion of a demo, the music from the previous level was played instead of the intermission music. +
[-]The "flashing hom" detector didn't work in GL mode. +
+ +
2.5.0.8 + @ 2010-Nov-13 + +
[+]Added support for textured automap. Available in the menu at "Options\Setup\Automap\Enable Textured Display". +
[+]"Next Level" key now works during normal play, and not just during demo playback. +
[+]Added "-shotdir" command line switch and "screenshot_dir" config variable. Default folder for screenshots is now exe_dir. +
[+]Two new sponsored HUDs. +
[*]Added ability (overrun_missedbackside_emulate 1) to emulate wrong memory access for cases when some linedefs have a two-sided flag set, but no second sidedef. Fixed desynch on CLNJ-506.LMP @ CHALLENJ.WAD +
[*]Broken BLOCKMAP lumps won't be recreated automatically. Use "-blockmap" command line switch if PrBoom-plus reports a buggy blockmap. +
[*]The "-solo-net" and "-emulate x.y.z" params are saved to the demo footer. +
[*]Added frustum culling for sprites if mouse look is enabled (gl_sprites_frustum_culling). If you look up or down on nuts.wad with this, frame rate will increase instead of decreasing. +
[*]Improved compatibility with Doom 1.2. All known Doom 1.2 demos including ret12na.lmp @ return01.wad, wp2-33.lmp @ wadpak2.wad and uac_dead demos by Never_Again are now in sync. +
[*]GLBDEFS alias for GLDEFS lump to avoid compatibility issues with other ports. Note that if GLBDEFS is present, GLDEFS will not be parsed at all. +
[*]Command-line added demos don't conflict with lump names anymore. For example, you can play map01.lmp back on map01. +
[*]Incorrect nodes on e1m1 @ deepcre.wad don't crash PrBoom-plus anymore. +
[-]The title screen from the Duke Nukem total conversion PWAD didn't appear in software mode, being replaced by a black screen. +
[-]Pitch-shift emulation didn't work. +
[-]On-screen graphics like PAUSE, M_DOOM, TITLEPIC, etc didn't fit in the screen at 16x9 aspect ratios and "not adjusted" mode for "status bar and menu appearance". +
[-]Fixed glitch in "no texture" mapping trick under invulnerability when using hires textures. +
+ +
2.5.0.7 + @ 2010-Aug-15 + +
[+]Added support for DeePBSP v4 extended nodes. +
[+]Added support for ZDoom's uncompressed extended nodes. +
[+]Animation blending. (Options \ General \ Texture Options \ Blend Animations) +
[+]Support for Quake2/Unreal style skyboxes. Their definitions (using GLDEFS lump) are GZDoom compatible. +
[+]Support for MUSINFO lump (dynamic music changing) from Risen3D. +
[+]Support for custom per-texture detail textures. Syntax of definition using GLDEFS lump: +
+detail
+{
+  (walls | flats) [default_detail_name [width [height [offset_x [offset_y]]]]]
+  {
+    texture_name [detail_name [width [height [offset_x [offset_y]]]]]
+  }
+}
+
+ +Where detail_name is a bmp/png/tga/jpg/pcx/gif lump between the HI_START/HI_END markers. You don't need to add the texture to TEXTURES1. +Default values are (width:16 height:16 offset_x:0 offset_y:0) + +

Example: +

+detail
+{
+  walls smooth01 32.0 //default detail for walls (width = 32, height = 16, offset_x/y = 0)
+  {
+    brick7  detstone 64.0 64 10.532
+    brick8  detail02 // detail02 16 16 0 0
+    water1 // do not apply default detail to water
+    water2
+    water3
+    water4
+  }
+  flats // no default detail for flats
+  {
+    grass1 Grass01 32 32
+    NUKAGE1 detslime 16 16 0 0 // different offsets for animated flats make sense
+    NUKAGE2 detslime 16 16 4 4
+    NUKAGE3 detslime 16 16 8 8
+  }
+}
+
+
[*]Hacked SDL_mixer.dll to avoid music bugs on some tracks. (for example: music goes wrong from 1:15 with timidity on doom2.wad map05) +
[*]Recalculates seg offsets that are sometimes incorrect with certain nodebuilders. Fixes among others, line 20365 of DV.wad, map 5. Makes sense only for software renderer. +
[*]Scan forward for a MUS header signature at the beginning of a MUS lump, which apparently DMX was capable of doing vis-a-vis DIESCUM.WAD. +
[*]Verifies blockmap validity and reports it in stdout.txt. +
[*]Disables sky texture scaling if the status bar is used. +
[*]In Chex Quest, uses the radiation suit colormap instead of the red colormaps that are usually used when taking damage (or getting the berserk pack). This matches Vanilla chex.exe behavior (from chocolate-doom). +
[*]Make -complevel 9 draw the sky normally during invulnerability. This is what Boom does. It was MBF that introduced the "negative" sky option. +
[*]No z-fighting in OpenGL mode for a killed mobj and its dropped weapon after death even with "sprites_doom_order 1". +
[*]The ability to render flats using draw lists. Can be noticeably faster on complex levels. "gl_use_display_lists" in config. +
[-]Fixed HOM at sectors 226 and 300 on E1M1 @ OTTAWAU.WAD. Fix affects only vanilla compatibility levels, because Boom's behavior is different. Newtechn.wad is also fixed. +
[-]Fixed a -trace_thingshealth issue. +
[-]Fixed a crash when demo footer and re-record are used. +
[-]Full path should not be stored in demo footer for Dehacked patches. +
[-]SDL_mixer's native MIDI music playing does not pause properly. As a workaround, it sets the volume to 0 when paused and "mus_pause_opt 1". +
[-]Boom colormaps are not be applied twice for hires textures anymore. +
[-]Fixed the latest known incompatibility with MBF. +
[-]Fixed an incompatibility with Win95. +
[-]During demo playback when using the camera the sound was heard from the players position, instead of the camera position. +
[-]After joining a game using recordfromto it was not possible to save the game. +
[-]Inheriting of friendlyness during spawn, for MBF compatibility. +
[-]PrBoom did not use the misc1 and misc2 frame parameters in A_Mushroom for Boom compatibility. Normally those two should control the angle and speed factor of the fireballs, but it didn't happen here. +
[-]"map_use_multisamling 0" did not work with "map_always_updates 0" +
[-]Fixed crash in software mode with 5x4 aspect. +
[-]Fixed mismatches between sotware and GL modes at 5x4 aspect. +
[-]Walkcam did not interpolate when paused. +
[-]Color variables were misaligned in the automap settings menu with "not adjusted" format. +
[-]A string of garbage from the title picture was left on the bottom of the HUD when using "not adjusted" format at resolutions with odd height (700x525 as example). +
[-]Fixed corruption of graphics after smoothing edges on systems with LP64 data model. Most Unix (like Solaris) and Unix-like systems (like Linux). +
+ +
2.5.0.6 + @ 2009-Dec-27 + +
[+]Added the ability to smooth sprites edges ("Options\General\OpenGL Options\Smooth Sprite Edges"). Might noticeably reduce frame rate. Includes a "gl_mask_sprite_threshold" config variable for the intensity of smoothing (0-100). +
[+]Ability to see kills/secrets/items/time statistics in automap mode. Available in the menu at "Options\Setup\Automap\". +
[+]Added a "map_use_multisamling" config variable to apply multisampling to the automap. +
[+]Added a "movement_shorttics" config variable in addition to the "-shorttics" command line switch. +
[+]Added a "gl_shadows_maxdist" config variable. +
[+]Added the ability to disable any OpenGL extension via the config instead of disabling all of them with "gl_compatibility" in case your drivers can't work properly with specific features. The extensions which can be disabled are: gl_arb_multitexture, gl_arb_texture_compression, gl_arb_texture_non_power_of_two, gl_ext_arb_vertex_buffer_object, gl_ext_blend_color, gl_ext_framebuffer_object, gl_ext_packed_depth_stencil, gl_ext_texture_filter_anisotropic, gl_use_stencil. +
[+]Added a "-trace_givendamage" command line parameter to trace the latest and total damage inflicted by any specific monsters or players in a level. If a player is selected, its data will be tracked across multiple levels. Available with the Boom HUD only. Usage: +
+    -trace_givendamage ThingID [ThingID] [ThingID]
+
+
[+]Added a "map_scroll_speed" cfg variable and GUI setting. "key_speed" doubles the speed. +
[+]Added a "sprites_doom_order" config variable for correct sprite sorting. See sectors 99, 115, and so on in Strain.wad map13, for example. This also fixes z-fighting between overlapped sprites in GL mode. +
[*]Mouselook key and keys bound to game speed change commands do not break cheats being typed. +
[*]Midi volume is applied to all midi devices when mus_extend_volume is 1. +
[*]Light level 0 will now be truly lightless with GZDoom lighting mode. +
[*]Automap grid can now rotate with map (from ZDoom). +
[*]Now DSSECRET is used when a secret area is found, instead of DSITMBK. The new sound lump is part of prboom-plus.wad and can be replaced through PWADs. +
[-]Fixed a crash in software mode at some resolutions when using 'Doom Format' or 'Fit to Width' for 'Status Bar and Menu Appearance'. +
[-]Now applies fake contrast when using "Fog Based" sector light mode. +
[-]Fixed Player's weapon lighting, that was too dark in GL mode. +
[-]Fixed small glitches on Boom HUD graphics in GL mode. +
[-]Mouse button now moves camera forward if you have it assigned to move forward. +
[-]Fixed software mode bug: if you played a wad with menu graphics big enough to go over the status bar, entering and exiting the menu left a trace of the menu graphic over the status bar. See HR2final.wad, for example. +
[-]Flat bleeding on walls won't incorrectly bleed through walls and floors when motion blur is in effect anymore. This fix requires support of the "GL_EXT_packed_depth_stencil" extension. +
[-]Simple shadows and transparent textures don't sometimes flicker anymore when fog-based lighting is used. +
[-]Shadows don't disappear in some situations anymore. +
[-]"-trace_thingshealth 0" now works. +
[-]Fixed screen shots at resolutions where width is not a multiple of 4. +
[-]Fixed slowdowns on Pentium 4 at 1024x768 (introduced in 2.5.0.3). +
+ +
2.5.0.5 + @ 2009-Nov-14 + +
[+]Added an "sdl_video_window_pos" config variable. Syntax: +
+    sdl_video_window_pos "center"
+    sdl_video_window_pos "100,100"
+    sdl_video_window_pos ""
+
+
[+]Access violation emulation for the "S1 Floor Raise Donut (changes texture) (9)" action in demo compatibility mode. "-donut floorheight floorpic" - takes custom values, overriding the program's default values ("-donut 0xF1000000 7", for DOSBox behavior). Disabled by default. +
[*]Updated to newest SDL libraries: SDL.dll v1.2.14, SDL_net.dll v1.2.9. +
[*]The "Sky Mode" GUI setting and "gl_drawskys" config variable have been removed. Now "SkyBox" is used if mouse look is enabled, "Standard Sky" otherwise. There is no more sky stretching with "SkyBox" mode. +
[-]Fixed stat screen animation for The Ultimate Doom and the Doom II cast BOSSBACK after MAP30 when using 'not adjusted' rendering. +
[-]Fixed detail walls when "GL_ARB_multitexture" extension is not supported or gl_compatibility is used. +
[-]Fixed bug where music doesn't change after IDMUS is used and a New Game is started. +
[-]Fixed bug where using the "End Level" key while a demo is paused breaks the "End Level" feature. +
[-]Fixed vertical scrolling of sky texture in SkyBox mode. +
[-]Now the mirror flag for MBF skies is used in SkyBox mode. +
[-]Fixed music playback on Windows Vista and 7. +
+ +
2.5.0.4 + @ 2009-Oct-07 + +
[+]Full support for wide resolutions. Aspect will be detected automaticaly, but you can force any ratio from the GUI. +
[+]Options\General\Status Bar and Menu Appearance: Not Adjusted / Doom Format / Fit to Width. +
[+]Added a -nocheats command line parameter and a deh_apply_cheats config variable to disable applying cheats from dehacked files. + +
[+]Added a key binding option for mouse look. +
[+]Added a "map_grid_size" config variable. +
[+]Added a "linear mipmap" texture filtering mode. +
[*]Less aggressive patch correctness checks. Makes sense for OBITUARY, where for some patches there is no column beginning exactly at the end of the column directory. +
[*]More permissive cheats handler for non-Boom complevels. The following DEH cheats now work: +
+    No Clipping 1 = !@#$%^&*() - you can't move in Boom with such a cheat
+    No Clipping 1 = bb - works incorrectly in Boom
+    No Clipping 1 = b/b - does not work at all in Boom, but works in vanilla
+
+ Making a short sequence on a cheat with parameters (idclevXX) will not work in vanilla (0-6) and lxdoom (10) complevels, as in the respective engines. + +
[*]Ignores sprite lumps smaller than 8 bytes (the smallest possible) in size - this was used by some dmadds wads to mark 'empty' graphic resources. Now you can play 22ventry.wad. +
[*]The Boom DEH parser has been restored. You can force it only for demo playback with -boom_deh_parser command line switch. +
[-]Fixed a crash on wads with incomplete REJECT table. (introduced in 2.5.0.2) +
[-]Fixed an incompatibility with Boom if you have player_bobbing 0 in cfg. (introduced in PrBoom 2.2.2) As a result, there is now a desynch on sewv12r-2135.lmp. Use -emulate 2.2.2 .. 2.5.0.2 for the old buggy behavior when playing such demos. +
[-]Fixed a crash related to A_BFGSpray with NULL target when using dehacked patches - discovered with insaned2.deh (thanks CSonicGo and fraggle). 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 it lacked proper memory protection. + +
[-]Fixed a crash (division by zero) in Boom HUD code for "Max ammo = 0" in a DEH. +
[-]Fixed flats drawing on a Mac in case of render_precise 1. +
[-]Fixed an overflow of the mapnames array (-iwad tnt.wad -file ht.wad -warp 33) +
[-]Avoids z-fighting between grid and walls on automap in GL mode. (introduced in 2.5.0.3) +
[-]Fixed a vanilla automap grid bug: losing grid lines near the map boundary. (from EE) +
[-]Fixed an issue where walls look full-bright from close up. Added a "render_old_lightmaps" config variable for 16 (old) or 32 (new) lighting levels. +
[-]Fixed an issue with fake floors. See the colored room in boomedit.wad, as an example. (introduced in 2.5.0.2) +
[-]Hi-res textures in TGA format are now loaded from PWADs. +
+ +
2.5.0.3 + @ 2009-Jul-11 + +
[+]Simple shadows, from Doomsday. +
[+]New "Fog Based" sector light mode which is very similar to software mode. If it doesn't work correctly, try updating your drivers or replacing your ATI, because per-pixel fog (GL_NICEST) doesn't work with some ATI cards or their drivers. +
[+]New map_always_updates config variable that can be toggled between updating unexplored parts in automap mode or the classic behavior. +
[*]No multisampling in automap mode. +
[*]Speed improvement in OpenGL mode: ~25% on nuts.wad, 7% generally. +
[*]Automap drawing optimisation in GL mode: 220 fps instead of only 30 on map05 @ dv.wad after IDDT IDDT. +
[-]Fixed middle texture bug on Claustrophobia 1024 map06, sector 39. +
[-]Fixed desynch on longdays.wad (-emulate 2.5.0.1/2 for viewing demos recorded with buggy versions). +
+ +
2.5.0.2 + @ 2009-May-09 + +
[+]Support for DeePsea's tall patch method: Now it's possible to use patches of any height. +
[+]Support for hi-res textures in wads. You should place them between HI_START/HI_END markers. +
[+]Fog in "gzdoom" lighting mode. +
[+]High Quality Resize modes. Scale2x/3x/4x are supported. Thanks to Benjamin Berkels. +
[+]An alternative way of sky drawing was added. "Sky Mode: Skybox". It's slower than the "Standard" method, but looks better if you use mouse look. Also, it's the only way to see skies properly on old hardware, like 3Dfx Voodoo Graphics, ATI 3D Rage Pro, and others which don't support "Environment Mapping". +
[+]Added a "gl_clear" config variable for clearing the screen between frames. This is only necessary when developing a map, to see HOMs. +
[+]Added a "gl_ztrick" config variable for very old hardware, like 3Dfx Voodoo, NVidia RIVA128, etc. If this toggle is enabled the game will not clear the z-buffer between frames. This will result in improved performance (~10%) but might cause problems for some display hardware. Makes sense with gl_compatibility 1 and gl_clear 0. Do not use it with TNT2 or better, as performance will be slower. +
[+]Now PrBoom-Plus can save all necessary playback information at the end of demos without loss of compatibility. With such demos you just need to type "prboom-plus demoname" or click on a demo from your browser, archives manager or explorer and prboom will make any additional work by itself without any assistance. PrBoom-Plus will check for a demo footer and if it is present, will read which IWAD, PWAD and DEH files were used for recording, will try to download necessary add-on files from the Internet and will play the demo for you. +

Currently, PrBoom-Plus saves the following information (with demo_extendedformat 1): +

    +
  • VERSION - version of DemoEx format (1.0) +
  • PORTNAME - port name and version (PrBoom-Plus 2.5.0.1) +
  • PARAMS - iwad, pwads, dehs and critical for demos params like -spechit +
  • MLOOK - mouse look data +
+

For the automatic downloading of files, one must specify the getwad_cmdline parameter in the PrBoom-Plus config file. Examples: +

    +
  • getwad_cmdline "D:\games\Doom2\getwad\getwad.exe" +
  • getwad_cmdline "C:\my_cool_app -download_pwad_from_somewhere %wadname% -here c:\doom2" +
+
[+]PrBoom-Plus can now apply Boom colormaps to hires resources. +
[+]Supports independent OpenGL filtering methods (nearest, linear, etc) for textures, sprites and patches. +
[+]It's now possible to change screen resolution from the GUI. +
[+]An option to see any monsters that are still alive through walls. By default it's toggled with "Numpad /". It has two modes: All sprites with living monsters or living monsters only. Works only in GL mode. +
[*]PrBoom will attempt to play a DEMO4 lump present in a wad immediately after DEMO3, but without exiting with the "W_GetNumForName: DEMO4 not found" error that the Plutonia and TNT executables get. This makes sense for Plutonia 2, which includes a DEMO4 lump that is played back during the demo cycle by vanilla Plutonia. +
[*]"PrBoom-Plus" title instead of "PrBoom" on the CREDITS screen page. +
[*]Doesn't alter the default skill level when the difficulty skill is selected from the New game menu. An explicit default skill option was added instead, shoehorned into the misc section of page 2 of the General menu. +
[*]Smoothing for the edges of textures with holes. (And not only for sprites, as before.) +
[*]Screenshots in "gzdoom" lighting mode use current gamma. +
[*]Reconfiguration in menu. +
[*]Better and faster mipmapping for modern hardware. Support for GL_ARB_TEXTURE_NON_POWER_OF_TWO extention. +
[*]Two service variables for developers were added: gl_color_mip_levels and gl_fog_color. +
[*]PrBoom-Plus doesn't use detail texture over hires textures. +
[*]Textures are reloaded after changing the "Override PWAD's graphics with Hi-Res" option. +
[*]Smart algorithm for more seamless flats drawing. In some situations this removes seams from hires flats (between sectors 125 and 126 on map20 @ doom2.wad, etc). +
[*]Speed improvement in OpenGL mode. +
[*]Can play any episode with The Ultimate Doom, Final Doom, Doom95 and DosDoom compatibilities and -warp command line switch. Now you can play E5M1 @ 2002ado.wad with "-warp 5 1 -complevel 3". The (vanilla) Ultimate Doom executable also allows this. +
[*]Some interpolation code was optimized. This fixes slowdowns on the beta of ndcp2.wad map02. +
[*]Speed improvements. Some timedemo examples for my hardware (Intel Core2Duo 3.0, NVidia GeForce 8800 GTS): +
Command line: [port] -geom 640x480 -window -nosound -file [pwad] -timedemo [demo] +
Files: epic.wad, +epic.lmp, +nuts.wad, +nuts.lmp, +dv.wad +
                          epic.wad   nuts.wad   dv.wad
+glboom  2.5.0             126        53         560
+glboom+ 2.5.0.1           234        130        640
+glboom  2.5.0.1.fast      277        134        705
+glboom+ 2.5.0.2           362        149        834
+
+prboom  2.5.0             174        115        400
+prboom+ 2.5.0.1           158        114        382
+prboom+ 2.5.0.2           191        121        424
+
[-]Fixed a rare wrong sprite rotation glitch when render_paperitems is 0. +
[-]Avoids segfaults on levels without nodes, and refuses to load maps with an incomplete pwad structure. +
[-]Avoids segfaults with unknown patch formats, and checks if the patch lump can be a Doom patch. +
[-]Gets rid of a line of graphics on some middle textures with holes. Sector #42 on gravity.wad as an example. +
[-]Doesn't render holes between flats and walls in some situations, on some hardware. Sector #557 @ map21 @ pl2.wad. GLBoom-Plus now renders per linedef instead of per seg. Performance has increased a little as a bonus. Also, some small texturing glitches are gone. See linedef #361 @ bbb.wad, as an example. +
[-]Gets rid of a half-pixel mismatch on seams between identical and correctly aligned textures in GL. This was an old GLBoom bug which is now fixed. +
[-]Fixed incorrect flipping for "Transfer Sky Texture" actions (272 <-> 271) in OpenGL. +
[-]Fixed issue with near-clip plane. Works only on NVIDIA. + +
From PrBoom +
-Don't set default skill level from the new game menu. Added explicit default skill option. +
+ +
2.5.0.1 + @ 2008-Nov-22 + +
[!]Updated to the latest prboom 2.5.0. (SVN build, revision 2936) +
[+]New hardware-mode algorithm for drawing flats into holes left by missing textures, greatly improving compatibility with software mode. This requires a stencil. This revision fixes at least one billion HOMs on various levels which use this software trick. Eternal.wad map28 as example. Old algo will be used with gl_compatibility 1 in cfg. Thanks to GrafZahl. +
[+]Experimental cygwin/mingw32 compilation support. +
[*]Resolution limitation is removed. Memory usage has decreased as a bonus. 7 mb instead of 8 on map01 @ doom2.wad and 14 mb instead of 18 on map05 @ epic.wad at 640x480. +
[*]launcher_enable variable now has three values: "never", "smart" and "always". "Smart" behaviour was default earlier. +
[*]Attempt to optimise screen pitch to reduce CPU cache misses in any resolution. +
[*]Do precache level with normal demo playback if level_precache 1 +
[-]Fixed rare crash in FixedDiv introduced in Boom. +
[-]Status Bar was affected by light level of the sector on some on-board Intel video cards. +
[-]PrBoom will not try to show (with following unexpected exit) the fourth episode menu item for pre-ultimate iwads. Makes sense for doom.wad v1.2, DOOM 1 shareware and DOOM 1 registered. (introduced in 2.4.8.3) +
[-]Re-fix vanilla imprecise calculation (vibrations) of the texture coordinates for flats in software renderer. +
[-]Fixed wipe in software. +
From PrBoom +
-Restore special case for trivial maps (bug #1837422) +
-Fix linear filtering on flats in software mode +
-Fix crash when an unknown sprite is used with a non-zero frame number +
-Fix crash by testing for null targets in mancubus fire code pointers +
+ +
2.4.8.5 + @ 2008-Oct-02 + +
[*]Set processor affinity mask under non-Windows platforms using the POSIX API (from chocolate-doom). This is a workaround for a bug in SDL_mixer that causes occasional crashes. +
[-]Fixed crash when the nodes used for in-wad demo playback are not the same as those used for playing. (introduced in 2.4.8.3) +
[-]Strange behaviour may result if a dehacked patch changed the music or sound lump names. (introduced in 2.4.8.2) +
[-]Fixed crash if a response file (i.e. @myargs.rsm) is used. +
+ +
2.4.8.4 + @ 2008-Sep-07 + +
[*]New application icon. +
[*]ENDOOM/ENDBOOM support using text mode emulation by fraggle. + +
[*]New algorithm for detection of fake flats and ceilings. It is much more correct and little bit quicker. Now glboom+ can detect complex systems from sectors without bottom/top textures and draw correct height, lighting and pic. Eternal.wad, map05, sectors 462 and 489 are simple examples; see sourceforge bug #2083354 for more examples. +
[-]Memory overrun in F_BunnyScroll in software mode. Introduced in 2.4.8.3. +
[-]Wrong flats drawing in software mode. Introduced in 2.4.8.3. +
+ +
2.4.8.3 + @ 2008-Aug-23 + +
[!]Updated to the latest prboom 2.4.8. (SVN build, revision 2741) +
[+]Support for DDS format of hires textures. + +
[+]Ability to change the level of anisotropic filtering from the in-game menu. +
[+]Real "black and white" effect for invul. It needs OpenGL 1.3. +
[+]Motion blur effect for strafe50. (gl_motionbloor 1 in config) +
[+]Ability to use only the allowed CPUs. It is necessary for buggy SDL_mixer (<=1.2.8 at least) on multi-processors at least win32 systems. process_affinity_mask in config. A process affinity mask is a bit vector in which each bit represents the processor on which the threads of the process are allowed to run. +
[+]gl_compatibility variable in cfg. Try setting it to 1 if you have any problems in OpenGL. All OpenGL extentions will be disabled in this case. +
[+]An alternative way of sky drawing was added (gl_drawskys 2 in cfg). This method make sense only for old hardware which has no support for GL_TEXTURE_GEN_*. Voodoo, for example. There are some bugs with alternative skies (see map12), but it is better than being without a sky at all. If you have red color instead of sky, then this ability is for you. +
[+]Ability to play wads with wrong SEGS lump if workaround is possible. Demo recording is not allowed on such levels. See e1m9 @ NIVELES.WAD as example. + +
[+]Additional sector light mode was added. (Options\General\Sector Light Mode - GZDOOM) This method has 32 levels of brightness instead of 5. There is new command-line switch "-resetgamma" for restoring original gamma after crashes. +
[+]Ability to play MP3 and OGG from wad. +
[+]New 'mixed' lighting mode for opengl. It uses gamma ramps like in gzdoom mode and it has 32 levels of gamma, but instead of changing device gamma it applies new gamma to textures. So it needs to reload all textures after change of gamma. +
[+]Added demo compatibility with chex.exe ("-exe chex" command-line switch) +
glboom-plus.exe -iwad doom.wad -exe chex -file chex.wad -deh chex.deh -playdemo chex1.lmp
+You must use chex.deh by fraggle
+http://chexmania.tripod.com/downloads.htm
+http://www.doomworld.com/idgames/?id=15420
+
[*]Win32 build is compiled with Microsoft Visual Studio 2008. +
[*]OpenGL: Original doom added/removed one light level for walls exactly vertical/horizontal on a map, but old code used 8 instead of 16. +
[*]More precise rotation of sprites if render_paperitems is set to zero. +
[*]Unsupported GL nodes will be ignored instead of closing PrBoom-Plus with an error message. + +
[*]Ability to play wads with wrong flat names. Unknown flats will be replaced with "NO TEXTURE" preset from prboom-plus.wad. +
[*]Make missing sounds non-fatal. Makes sense for doom.wad v1.2 to skip some absent sounds. +
[*]Intercepts overrun code should work properly on big endian machines as well as little endian machines. +
[*]The title screen music in deca.wad is a corrupted mus that nonetheless somehow manages to play properly in Vanilla Doom. Now you can hear such music in PrBoom-Plus too, because new mus2mid code is capable of converting the corrupted mus when MUS header checking is disabled. +
[*]MBF Sky property-transfer linedef types will be applied for boom compatibility again. It was a bad idea to disable it. +
[*]Advanced syntax for -geom command-line switch. Syntax: -geom WidthxHeight[w|f], w - windowed, f - fullscreen. Examples: -geom 320x200f, -geom 640x480w, -geom 1024x768 +
[*]Speedup in software renderer. + +
[*]"Rendering Quality" now is available in software mode too. "Quality" mode can kill some "slime trails", but it works slower. In the previous version of PrBoom-Plus the "Quality" mode was forced. +
[*]Lower memory usage if colormaps are used in OpenGL. +
[*]Speedup of level reloading in OpenGL mode (savegame, loadgame, loadgame...). Loadgame from the current (same) level now is much faster (100x). It is very noticeable on big levels like MAP05 @ epic.wad. +
[*]There is no longer a need to restart glboom-plus after a change of texture filter (mipmap, aniso) +
[*]More accurate patch drawing. Predefined arrays are used instead of dynamic calculation of the top and bottom screen coordinates of a column. New algo makes sense for small elements of the HUD (digits) in resolutions which are not multiple of 320x200. +
[*]"-videodriver" switch choice is saved in cfg. +
[*]For pre-Ultimate Doom complevels, the fourth episode is not allowed. +
[-]Fixed crash after "reset to defaults" from in-game menu. It's an old bug and all the previous versions of prboom worked without crash only by luck. + +
[-]Fixed bug if translucency percentage is less than 50, in which case all translucent textures and sprites disappeared completely. +
[-]Disabling transparency in OpenGL for original sprites which are not changed by dehacked, because it does not work correctly in cases when transparent sprites are behind transparent walls. Global sorting of transparent sprites and walls is needed. +
[-]Mouse look did not work on automap in overlay mode. +
[-]Fix bug with bringing up the launcher with SHIFT key. +
[-]Rooms you haven't visited and are behind closed doors (with no windows) show up on the automap when you walk up to the doors without opening them. This bug was introduced in the 2.4.8.2 release with new clipper code. +
[-]Fixed problems with association of PrBoom-Plus with DOOM demos if you are not Administrator. Makes sense for Vista. +
[-]Fix bug with sky at the bottom part of an imaginary skybox around a level if mouse look is used. Map30 @ "Deus Vult II: First Edition" is a good example. +
[-]OpenGL: More correct (similar to software) drawing of skies with height more than 128 if mouse look is not used. +
[-]Fix random color of pixels instead of black during first wiping on fullscreen in software mode. + +
[-]Bug with sprites on big-endian systems like Apple's Macintosh line prior to the Intel switch. +
[-]Fixed error in OpenGL when upper texture on the linedef with "Transfer Sky Texture" action is not set ('-'). 'ASHWALL1' with zero index must be forced in this case, which I did for normal not transferred skies some time ago. +
[-]Fix vanilla imprecise calculation (vibrations) of the texture coordinates for flats in software renderer. Visplanes with the same texture now match up far better than before. You can see this bug on cchest2.wad\map02 in the room with moving illumination around the room with sector 265. The new algo is ~1% slower than the original and will be used only if render_precise is 1. +
[-]Fixed wrong processing of the "Blue Armor Class" and "Green Armor Class" strings from a DEH. +
[-]"Flashing HOM indicator" option did nothing. Fixed. +
[-]Fixed crash between e1m4 and e1m5 on chex.wad. +
[-]Key for entering setup menu had no entry in config. +
[-]The supersecret "COOL!" area of Kama Sutra map15 had a rather ugly texturing bug (GL only). + +
[-]There is no longer a line of graphics under certain weapons in GL. + +
From PrBoom +
-Added high color rendering +
-Ignore switches that reference unknown textures instead of exiting +
-When using spy mode in a multiplayer demo in demo_compatibility mode, play the expected player's pickup sounds (the currently displayed player, not the player at the console when the demo was recorded) +
-Fix crash when loading maps with missing player starts. +
-Fix crash when reloading network savegames. +
-Fix bug in transparency rendering caused by doing it in two places. +
-Fix the colour of player 4 (red) in multiplayer. + +
-Fix position of netgame player arrows on automap in rotate mode. +
-Always draw player arrows on the automap in deathmatch demo playback. Previously they'd only be drawn if you played the demo from the command line. +
-Ignore chat key presses in demo playback and in -solo-net +
+ +
2.4.8.2 + @ 2007-10-16 + +
[!]Updated to the latest prboom 2.4.8. (SVN build, revision 2423). +
[+]New mouse code without SDL lags. Win32 mouse handling was removed. +
[+]Boom colormaps are supported in OpenGL mode. +
[+]New mus -> mid conversion code thanks to Ben Ryves benryves@benryves.com This removes bugs and plays back a lot of music closer to Vanilla Doom - eg. tnt.wad map02. +
[+]Smooth movement: interpolation of weapon bobbing. +
[+]New command-line switch "-emulate prboom_ver" for emulation of old buggy behaviour of previous prboom versions during playback of doom\doom2\boom\mbf\lxdoom demos in cases where a prboom bug affects the demo compatibility and leads to desyncs in the original EXEs. Now you can specify only "-emulate 2.2.6" instead of set of -force_remove_slime_trails, -force_boom_brainawake, etc. For example, boom-incompatible demo by Anima Zero cc2lmps.zip\c215-uv.lmp recorded with prboom 2.2.4 in boom compatibility mode can be correctly viewed in newer versions with -emulate 2.2.4 or with -force_prboom_friction. +
[+]PC Speaker emulation from chocolate-doom. +
[+]New command-line switch "-force_lxdoom_demo_compatibility" for emulation of all bugs in demo compatibility mode in lxdoom. There are no more desynchs on "Doom-incompatible" demos from DHT6 exams. +
[+]Ability to force -nomonsters and -respawn for playback of 1.2 demos. Demos recorded with Doom.exe 1.2 did not contain any information about whether these parameters had been used. In order to play them back, you should add them to the command-line for playback. There is no more desynch on mesh.lmp @ mesh.wad (prboom -iwad doom.wad -file mesh.wad -playdemo mesh.lmp -nomonsters) +
[+]There is a new command-line switch "-shorttics". This makes it possible to practice routes and tricks (e.g. glides, where this makes a significant difference) with the same mouse behaviour as when recording, but without having to be recording every time. +
[+]Wipe screen effect in OpenGL. +
[+]Support for Hi-Res textures and patches in OpenGL. +
[+]Import the clipper code from gzdoom. Thanks to GrafZahl. PrBoom uses a clipper with screen pixel precision. It isn't precise enough for GL. For GL we need a clipper with angular precision to avoid some kind of glitches. +
[*]Events queue will not be cleared at the start. (like vanilla does) +
[*]"No Quit Sound" is renamed to "Fast Exit". EndDoom screen will be skipped if this option is turned on. +
[*]Effect of invulnerability uses a colormap in OpenGL mode instead of hard-coding. See nuts.wad as example. +
[*]"Sky is unaffected by invulnerability" compatibility setting (comp_skymap) works in OpenGL mode now. +
[*]"Paper Items" setting has no effect for hanging things. +
[*]The player's weapon is displayed much darker than in 'vanilla' and other ports. In this version the mistake is corrected only for software rendering. +
[*]Improved support for Doom v1.2: Projectiles, such as monster and player plasma and rockets, were able to activate down-wait-up type lifts in versions of Doom up to v1.2. +
[*]Monsters on automap are drawn on top of other things to avoid situations when red triangles (live and countable monsters) are hidden by the green triangles for non-countable things. +
[*]Show a warning if savegame is from the previous version of prboom-plus. +
[*]"-recordfromto" works for all levels of compatibility. +
[*]Improved precision. There are no more HOM's on maps with big open areas: nuts.wad at some positions, thespir2.wad at the start, Generator of Evil.wad in last room, MAP05 @ epic.wad. everywhere, etc. +
[*]Huge speedup in OpenGL on levels with sectors which have many lines. I've got 87 fps (timedemo) on MAP05 @ epic.wad in this version instead of 64 in 2.4.8.1. +
[*]Compatibility with common mapping errors "linedefs w/o tags apply locally" works with Walk and Gunfire triggers (not only with switches) as in (G)ZDoom. +
[-]Non traditional menu was removed to avoid problems with, e.g., Alien Vendetta. +
[-]Launcher: wrong strings in history combobox. +
[-]Fixed Boom incompatibilities. There are no more desyncs on Donce's demos on horror.wad +
[-]Fixed Boom incompatibilities. Original friction and bobbing code was restored. There are no more desyncs on cybart.lmp @ Artica.wad and dismlmp.lmp @ dismem.wad. "-force_prboom_friction" for emulation of old buggy behaviour. +
[-]%DOOMWADDIR% had no effect for PWADs and DEHs. +
[-]A strip of rendered graphics could sometimes be seen under the status bar, along the bottom during intermission screens and in the rollcall screen. +
[-]With OpenGL rendering, many of the translucent windows in MAP29 @ cchest2.wad are visible from one side only. +
[-]Boom bug: Fixed incompatibility with original EXE's (complevels 0..6) on levels with "Monster Spawner". "-force_boom_brainawake" for emulation of old buggy behaviour. There is no more desynch on 300340HR.lmp @ RlmChaos.wad. +
[-]When radsuit ends, palette changes w/o warning. +
[-]When playing back with -recordfromto, and before resuming recording, pausing causes playback to desync. +
[-]OpenGL: Fix an issue with lines (boxes) which appear around the elements that change on the intermission screens in Doom1. Thanks to Graf Zahl. +
[-]When a file with the .BEX extension is used in an autoloading pattern, the program fails to load it. +
[-]Boom bug: Correction for DEHs which swap the values of two strings. For example: Text 6 6 - RUNNINSTALKS; Text 6 6 - STALKSRUNNIN. There are no more bugs on AllHell.wad. +
[-]Kills percentage, if extremely high, was reported wrongly and could in theory lead to desyncs. +
[-]PrBoom bug: Par times were not displayed. The program was viewing prboom(-plus).wad as a pwad, and therefore the piece of code that suppressed par times was always being triggered. +
[-]Sometimes the pistol sounds that play while the tallies are being totaled on the end screen will play for too long after the completion time has finished tallying. +
[-]There is no more crash on e1cmnet3.lmp between e1m2 and e1m3. +
[-]Smart totals: Monsters spawned by an Monster Spawner should not be countable for total killed. +
[-]Avoid crashes at end of demos if DEMOMARKER (0x80) does not exist at the end. +
[-]ScreenShot feature did not work with "-videodriver directx" on fullscreen software. +
[-]There are no more visual glitches with sky on MAP14 @ Icarus and MAP20 @ Hell Revealed. GZDoom still has this bug. +
[-]Fix of no warning (flashes between shadowed and solid) in OpenGL when invisibility is about to go. +
[-]Fixed wrong finding of next highest floor (linedef action 18, 20, 131) for demo compatibility. There are no more desynchs on napalm.wad. +
[-]The priority system is broken in the ouch face code in a way that makes the face not work with monster damage. +
[-]Move mouse sensitivity menu upwards a little to avoid overlap with status bar with accompanying visual glitches on software renderer. +
[-]The bug in the algorithm for splitting of a sector into closed contours was fixed. There is no more HOM at the starting area on MAP16 @ Eternal.wad in OpenGL. I hope nothing was broken. +
[-]Correction of an overflow in calculation of a sound origin on large levels. New code is applied only for non-compatible modes and with comp_sound equal to zero. There is no more bug with an absent sound after pressing the distant western switch on Hell Spirit (not released) by Alexander "Eternal" S. +
[-]Premature program exit during map26 in 4-player coop demo 29uv4p_*.lmp @ doom2.wad. This bug was introduced in r1458 by cph for 2.4.3 release. +
[-]Wrong calculation of weapon width in OpenGL in some cases, especially during movement. Shotgun in DSV3_War looks correct now. +
[-]OpenGL: Blink during demoskip on fullscreen. +
[-]OpenGL: Bug with duplicated lines, e.g. MM.WAD Map22 lines 1298 and 2397. There is no more HOM on Memento Mori MAP22 sector 299. +
[-]Fixed slowdown during screen melt at 1024x768 on some systems. +
[-]Transferred sky texture on scrolled wall were not scrolled with mouselook or changed FOV. +
[-]Sky property-transfer linedef types (271, 272) should not be applied for BOOM complevels. + +
+ +
2.4.8.1 + @ 2006-12-26 + +
[!]Updated to the latest prboom 2.4.8 (SVN build, revision 2355). +
[+]Compatibility with common mapping errors: "Walk underneath solid hanging bodies". Thanks to RjY. +
[+]Launcher: Autoselecting of wads according to the lmp file-name (using regular expressions) +
[+]Two new compatibility options are added: +
    +
  • comp_ouchface - "Use Doom's buggy 'Ouch' face code" +
  • comp_oofsound - "Dead players make 'oof' sound when landing" +
+
[+]New "-auto" command-line switch for autoloading of wads according to the lmp file-name. +
[+]There are three modes of sprite clipping in opengl: +
    +
  • Constant - gl_sprite_offset used; +
  • Always - if the sprite is below the floor, and it's not a hanger/floater/missile; +
  • Smart - if the sprite is below the floor, and it's not a hanger/floater/missile, and it's not a fully dead corpse; +
+
[+]Demo progress bar works during skipping. +
[+]Improved handling of unrecognized demo formats. Legacy and Boom v2.00 demos do not crash PrBoom-Plus now. +
[+]"Use" key ("space" by default) during skipping will show the current frame. +
[+]New "all-in-one" version that includes the resource wad and SDL files inside the exe. +
[*]Compatibility with common mapping errors "Pad a too short reject with zeros" was removed. Added new command line switch "-reject_pad_with_ff". PrBoom 2.2.5 and 2.2.6 padded a short REJECT with 0xff (ones) instead of zeros. This command-line switch may be needed for demos recorded with these versions of PrBoom on maps with a too short REJECT. For example: equ7-108.lmp@lwfirst.wad. +
[*]Improvements in text labels: +
    +
  • "All boss types can trigger tag 666 at ExM8" -> "Emulate pre-Ultimate BossDeath behaviour" +
  • "Pain elemental limited to 20 lost souls" -> "Pain elementals limited to 21 lost souls" +
  • "Ultimate Doom v1.9" -> "Ultimate Doom/Doom95" +
  • "Final Doom/Doom95" -> "Final Doom" +
+
[*]Removing startup delay during recording. You can record demos from the first tic now. +
[*]Correction in "smart items clipping": a not fully dead corpse should be moved up, exploding barrels for example; a missile should not be moved. +
[*]Improved support for Doom v1.2: +
    +
  • Max Health Bonus = 199; +
  • Max Armor = unlimited; +
  • Max Soulsphere = 199; +
  • Megasphere Health = 199 (there was no megasphere back then, but this seems consistent); +
  • Lost souls "Affect Kill %"; +
  • Monsters could commit suicide if they damaged themselves by exploding a barrel; +
+
[*]"Total Level Times" are saved with any level of compatibility. +
[-]Launcher: WADs not containing levels were not displayed in the files list. +
[-]GLBoom bug: Things disappeared behind translucent things sometimes. gl_sortsprites has no effect now. +
[-]GLBoom bug: Wrong (with holes) drawing of the background for the loadgame and savegame entries. +
[-]GLBoom bug: Wrong alignment of texture with 'lower unpegged' flag on two-sided walls in upper part. Linedef 527 on STRAIN.WAD/MAP09 for example. +
[-]Eliminating the six-tic jump when changing speed. +
[-]Boom bug: Generalized effects could work instead of failing in a 'vanilla' compatibility mode. "-force_truncated_sector_specials" for emulation of old buggy behaviour. There are no more desyncs on map06@vv2.wad, etc. +
[-]Boom bug: Fix of hanging decoration disappearing in Batman Doom MAP02. The killough's math should be rechecked there. +
[-]PrBoom bug: Pausing during intermission in playback could cause desynchs. +
[-]PrBoom bug: Bug in handling of common strings in deh files introduced by cph in PrBoom 2.4.2. +
[-]PrBoom bug: comp_doorstuck should be forced (but was not) for boom201 compatibility. There is no more desync on demp-208.lmp@DEMONIPA.WAD. +
[-]PrBoom bug: The behaviour with comp_666 was that the death of all barons on E2M8 or E3M8 ends the level, heh. This is incorrect; it should have no effect. The bug was introduced in 2.4.x by cph. After some tests and disassembling I conclude that the implementation of A_BossDeath() in all compatible ports (PrBoom, Eternity, Chocolate-Doom) is totally wrong for doom2 compatibility. This version should fix the buggy behaviour. There is no more desync (premature exit) on episode3.lmp@doom.wad +
[-]Predefined translucency setting (comp_translucency) could override deh settings. +
[-]Sometimes the figures for kills, items, etc., stopped getting updated. +
[-]PrBoom bug: Fixed Boom v2.01 incompatibilities. There are no more desyncs on demp-800.lmp@demonipa.wad, tomb.lmp@tomb.wad, ven-1116.lmp@venom.wad. +
[-]PrBoom bug: Fixed an MBF incompatibility. There is no more desync on v2-2822.lmp@vrack2.wad. "-force_no_dropoff" command-line option is for mbf_compatibility demos recorded with prboom 2.2.2 - 2.4.7 in case of desyncs. +
+ +
2.4.6.1 @ 2006-09-16 + +
[!]Updated to the latest prboom 2.4.6 (SVN build). +
[+]Mac specific: OpenGL version is available for Mac too. +
[+]Holding down SHIFT during start-up to bring the launcher up even if you disable it in settings. +
[+]Option to disable "doubleclick as use" behaviour. +
[+]Option "Max View Pitch" for restriction of the maximal looking up/down angle. (0-90) +
[+][-spechit X] command-line switch allows the user to provide a spechits magic number, overriding the program's default value. +
[+]Compatibility with common mapping errors: "Pad a too short reject with zeros". +
[+]Autodetect of unconverted tasdoom.exe demos. +
[+]Options for showing progress bar during demo playback and demotime/totaldemotime style as alternative. +
[+]New Screen Multiple system (like in chocolate-doom) and Interlaced Scanning for this. (emulators and TV style) +
[+]New [-videodriver name] command-line switch (sdl_videodriver variable in config) for setting up the videodriver name that SDL will use. The "windib" video driver is the default now, to prevent problems with certain laptops, 64-bit Windows, and Windows Vista. The "directx" video driver is forced by PrBoom-Plus only for software rendering on Win9x systems to prevent some problems on out-of-date systems. Also, you can use the "default" value to force PrBoom-Plus behavior by default. +
[+]Possibility to enable/disable the Launcher from its window. +
[+]Launcher: iwads are now shown in the files list so that they can be used as pwads +
[*]POSIX specific: Name of the dot directory was changed from .prboom+ to .prboom-plus +
[*]Default key for taking over the controls with resume recording feature is changed to 'q'. Default key for switching between chasecam/walkcam/normal views is changed to 'KeyPad0'. +
[*][-playdemo demo -warp X -skipsec Y] will skip Y seconds on X level. +
[*]Reconfigurations in settings. Most of the PrBoom-Plus settings are moved to General. +
[*]Config entry for "uncapped framerate" is renamed from "movement_smooth" to "uncapped_framerate" as in upcoming PrBoom. +
[*][-net1] command-line option renamed to [-solo-net] as in PrBoom. +
[*]Uncapped framerate: speed update up for HUD on slow systems. +
[*]Forcing "directx" video driver for software rendering in Win9x. +
[*]Alt mouse handling: Do not grab mouse if the application has no keyboard focus. +
[*]Alt mouse handling: Disabled for win9x at full screen modes. +
[*]Alt mouse handling: reconfigured as a "Mouse handling" option, with the name "Win32". The relevant config variable is "mouse_handler" (0=SDL; 1=Win32). +
[-]Mac specific: Mouse did not work with "alt mouse handling". Disable this feature for non-windows platforms. +
[-]Fix a crash after pressing the walkcam key at the intermission screen, the game final animation, or a demo. +
[-]IDRATE cheat shows correct FPS with uncapped framerate. +
[-]Fix for incorrect chasecam/walkcam sight after loading of the next level. +
[-]Fixed crash with newest sdl_mixer.dll after skipping level during playdemo. +
[-]GLBoom: Correction of display (HOM) of the textures with holes (MIDBARSX, MIDGRATES, etc.) on one-side walls in OpenGL. fenris.wad is an example. +
[-]PrBoom bug: Fixed crash with zero-size BLOCKMAP lumps. +
[-]PrBoom bug: Fix compatibility issue with building stairs. There is no more desync on ic29uv.lmp@icarus.wad +
[-]PrBoom bug: Fix a compatibility issue that was introduced by Lee Killough a long time ago. There is no more synch :) on dv04-423.lmp@dv.wad. Added switch to force old behaviour with demo_compatibility: [-force_remove_slime_trails]. It is relevant for viewing doom-compatible demos recorded with old versions of prboom (< 2.4.6) or prboom-plus (< 2.4.6.1) in cases of desyncs. +
[-]PrBoom bug: Fix another compatibility issue. It corrects half of the incompatibility shown by w303-115.lmp@Wotdoom3. +
[-]Boom dehacked support: Correction of wrong processing of "Respawn frame" entry. Highly relevant to wads such as Wotdoom3 and any others that change this setting. There is no more synch (again) on w303-115.lmp@Wotdoom3. +
[-]PrBoom bug: Fix compatibility issue with ts27-137.lmp@buggy MBF behavior (3-key door works with only 2 keys). There is no more desync on 10sector.wad. +
[-]PrBoom bug: Wrong scaling of status bar at some resolutions. Strongly noticeably on ARMS section at 320x240. +
[-]Alt mouse handling: Ungrabbing of mouse was not being done before a quit in some cases. +
[-]PrBoom bug: Fixed a tasdoom incompatibility. There is no more desync on e4tux231.lmp@doom.wad. Nowhere no hide. All demos from the original TAS site now play back with PrBoom-plus. +
[-]PrBoom bug: Conflicting command-line parameters could cause the engine to be confused in some cases. Added checks to prevent this. Example: glboom-plus.exe -record mydemo -playdemo demoname +
[-]Launcher: Fake iwads could be in the game list. Added a check for "IWAD" signature to prevent this. +
[-]PrBoom bug: Weird texture problem. (spacelab.wad - GL only) +
+ +
2.4.3.1 @ 2006.07.25 + +
[!]Updated to the latest prboom 2.4.3. +
[!]PRBoom-Plus is now available on UNIX, Linux, and other POSIX systems and also on Mac. +
[+]Several predefined cfgs and batch launcher for them are included. +
[+]Simple aspect ratio adjustment made posible in OpenGL. It may be useful for people with unusually shaped monitors. Some usage examples: +
+glboom-plus.exe -aspect 320x240
+glboom-plus.exe -aspect 2x1
+
+
[*]Changes in processing of spechit overflow for correct playback of some Xit's demos on scythe2.wad/map02. +
[*]Roll back to old SDL library. Some people had some problems with new. +
[*]The maximal supported resolution is increased from 1600x1200 up to 2048x1536. +
[*]GLBoom will try to set the closest supported resolution if the requested mode can't be set correctly. For example glboom.exe -geom 1025x768 -nowindow will set 1024x768. It affects only fullscreen modes. +
[*]The maximum walking side speed for strafe50 is 0x19. +
[*]The prboom's savegamename mask "prbmsav" was renamed to "prboom-plus-savegame". Also: +
+prboom_server.exe -> prboom-plus_server.exe
+prboom.exe -> prboom-plus.exe
+glboom.exe -> glboom-plus.exe
+prboom.cfg -> prboom-plus.cfg
+glboom.cfg -> glboom-plus.cfg
+prboom.wad -> prboom-plus.wad.
+
+
[-]Fix from Eternity (adapted by RjY) for ancient doom software render overflow that happens when the player goes over a linedef between two sectors of extreme height. There are no more crashes on thespir2.wad. +
[-]At use of the keyboard or joystick for strafing, prboom+ always generated strafe50 tics on turns independently of value of a corresponding option. +
[-]The launcher did not select last entry from history after start. +
[-]Problems with sky on thespir2.wad in opengl. +
+ +
2.2.6.29 @ 2006.06.08 + +
[+]Detection and emulation of spechit overflow for dosdoom and tasdoom compatibility. There are no more desyncs in the following dosdoom demos: flsofdth.wad\fod3uv.lmp, hr.wad\hf181430.lmp, hr.wad\hr181329.lmp, icarus.wad\ic09uv.lmp +
[+]Support for comp_666 and comp_maskedanim. +
[!]Update to newest SDL libraries. SDL.dll ver.1.2.10.0, SDL_mixer.dll ver.1.2.7.0, SDL_net.dll ver.1.2.6.0 +
[!]Some variables specific to OpenGL have been removed from the list of variables meant for prboom.cfg +
[!]The launcher will not be displayed if IWAD or PWADs are specified in a command-line. +
[!]The new PrBoom-plus entries in the configs are preceded by a title. +
[-]PrBoom bug: The sky in the third episode of Requiem was not drawn. It did not work correctly because the SKY3 has a zero index in the TEXTURE1 table. Textures with a zero (FALSE) index are not displayed in vanilla - AASHITTY in doom2.wad for example. But the sky textures are processed by different code, which does not have this bug. This bug is also present in the current GZDoom, but it only draws the wrong sky instead of drawing no sky at all. +
[-]PrBoom bug: The "Show coordinates of automap pointer" setting has no effect. +
[-]FOV had side effect on software rendering. +
[-]Alt mouse handling: Eternal rotation of the player at non-standard modes on software mode. Example: + +
prboom.exe -geom 1025x768 -nowindow +
+
+ +
2.2.6.28 @ 2006.05.09 + +
[+]Option to turn off "Paper Sprites". It works only with mouselook, and is only for non-solid sprites. +
[!]The command-line parameter "-screenscapture" is renamed to "-avidemo". Also syntax has changed. +
[!]Reconfigurations in settings. +
[-]Screenshots now use correct palette in software mode. +
[-]Screens capturing: do not dump black screens while in skip mode. +
[-]Respawned monsters on nightmare do not have countable flag now. +
[-]Mouse-induced Strafe50 couldn't be achieved. +
+ +
2.2.6.27 @ 2006.04.05 + +
[+]PrBoom compatibility: two new options for intercepts overflow detection. It detects and tries to emulate overflows on some odd demos like blackbug.lmp, manorbug.lmp, hr27odd.lmp, av08-odd.lmp, etc. +
[+]PrBoom compatibility: two new options for playeringame overflow detection. It detects and emulates overflows on vex6d.wad\bug_wald(toke).lmp, etc. It works without crashing in Eternity and chocolate-doom only by good fortune. +
[+]Cheating: new command line switches: -trace_thingshealth, -trace_thingspickup, -trace_linescross. Usage: +
+-trace_thingshealth ThingID [ThingID] [ThingID]
+-trace_thingspickup ThingID [ThingID] [ThingID]
+-trace_linescross   LineID  [LineID]  [LineID]
+
+
[+]Autorun: the "RUN" key inverts the autorun state +
[!]PrBoom compatibility: minor changes in processing overflow of spechit array. It became possible after decompiling of DOS EXE. +
[!]Names of variables responsible for overflows and their default value have been changed. It's switched on for emulation and switched off for warnings now. +
[-]Smooth turns in demos: movements of first player in a net-demo were not smooth if the second player is dead. +
[-]GLBoom: from certain angles and distances, things behind a translucent texture become invisible (test). +
[-]Boom bug: desyncs after using the key for switching to SSG directly with demo compatibility. This bug is present in current Eternity too. Thanks myk for the detailed information about it. +
[-]Launcher: the selected IWAD was loaded after preloaded wads. +
[-]Detail texture: detail texture moves slower than scrolling floors. +
[-]Detail texture: fixes for old videoadapters which do not support gl_arb_multitexture extention (still buggy). +
[-]Impossible to change game speed when viewing a net-demo (2.2.6.25). +
+ +
2.2.6.26 @ 2006.01.31 + +
[+]Launcher. Activated when no wad is specified in the command line. +
[+]Smooth movement: scrolling textures are interpolated. +
[+]Spechits overflow lines are listed in prboom.exe. +
[+]New command-line option (-net1) for single-player coop mode that works for play, recording and playback +
[+]New command-line options for setting a window (-window) or fullscreen (-nowindow) mode temporarily which is not saved in cfg. +
[+]Compatibility option to disable translucency applied to certain Things. +
[*]Spechit overflow: the size of emulated overflow is limited by 14 lines. +
[*]"Smooth movement" renamed to "Uncapped framerate". +
[-]PrBoom bug: bug in MBF compatibility mode (version number written wrongly in lmp header when recording). +
[-]PrBoom bug: translucency via dehacked/bex doesn't work. +
[-]PrBoom bug: DEH files preloaded in wrong order. +
[-]PrBoom bug: "Tagged doors don't trigger special lighting" handled wrongly. +
[-]prboom-server bug: -1 => 255 instead of maxcompat (14) +
[-]Alt mouse handling + OpenGL + Capped framerate + Fullscreen + server = mouse turning disabled (2.2.6.24) +
[-]Bug with DEH-files in BEX-Format (2.2.6.23) +
+ +
2.2.6.25 + +
[+]PrBoom compatibility: two new options for detection of overflow of "REJECT". It's emulated successfully if the size of overflow no more than 16 bytes. No more desync on teeth-32.wad\teeth-32.lmp. +
[+]Compatibility with common mapping errors: "Linedefs w/o tags apply locally". If a linedef type that normally requires a tag (e.g. a remote door) has no tag, then if possible apply the linedef's action to the local sector. Not available for recording, playback or with demo_compatibility. +
[+]Compatibility with common mapping errors: "Use passes thru all special lines". Boom's passthru flag is applied to all special lines. This makes it possible, for instance, to press switches that have been placed behind special lines such as scrolling textures. Not available for recording, playback or with demo_compatibility. +
[+]Compatibility with various early Doom versions and ports (for playback only, overriding autodetect): +
+Doom & Doom2, v1.666 : -complevel 1
+Doom2 doom2.exe v1.9 : -complevel 2 ;No more desync on uac_dead.wad\uac_dead.lmp.
+Ultimate Doom v1.9   : -complevel 3
+Final Doom doom2.exe : -complevel 4
+DosDoom              : -complevel 5 ;No more desync on demos from old TAS site
+TasDoom              : -complevel 6 ;and some of Andy Olivera's demos.
+
+
[!]Network game works again. +
[!]Allowing to mouselook during recording. +
[!]Interdiction of change of game speed during network game. +
[!]PrBoom+ will not show the advanced statistical information (Smart Totals) in deathmatch. +
[!]If file already exists, don't try to continue existing demo with demo_compatibility. +
[-]Crash with zero-length sounds. +
[-]Bug with DEH-files in BEX-Format (2.2.6.23) +
[-]Smooth movement: elevators were not interpolated. Sector #137 @ dmdjm02.wad for example. +
[-]Boom dehacked support: wrong processing of Max Health. A new compatibility option has been added so that it applies only to health potions. Highly relevant to wads such as Hacx, Wotdoom3, Anadream, and any others that change this setting. +
[-]Issue with dehacked floating monsters in glboom.exe if "Smart Items Clipping" is turned on (2.2.6.20) +
+ +
2.2.6.24 + +
[+]Option for removing the "wipe" effect. +
[+]Option for smoother mouse control. +
[+]Mouse acceleration code. +
[+]Ability to use savegames while using a complevel. +
[!]PRBoom compatibility: changes in processing overflow of spechit array. +
[!]The "-recordfrom" switch is renamed into the "-recordfromto" +
[-]PRBoom.exe: the screen ceases to be updated after start of new game while you are already in game. +
[-]Problems with status bar in prboom.exe (2.2.6.21) +
[-]-skipsec broken (2.2.6.23) +
[-]Few improvements in the sky rendering. No more HOM in the starting area of Memento Mori map29 and an minor issue in map30. +
[-]Translucency won't change until you restart the engine. +
+ +
2.2.6.23 + +
[+]Dehacked support: Monsters infight. +
[-]PrBoom: It crashes when you set game speed over 1000. +
[-]"-warp" switch didn't work. (the bug was introduced in 2.2.6.22) +
+ +
2.2.6.22 + +
[+]PRBoom compatibility: two new options for spechit overflow detection. It detects desyncs like compet-n\hr.wad\hr18*.lmp, all strain.wad\map07 demos etc. +
[+]Added "-levelstat" switch for levelstat.txt output like this: +
+E4M1 - 0:15.91 (0:15)  K:  8/63   I:  5/21  S: 0/2
+E4M2 - 0:10.97 (0:25)  K:  8/76   I:  6/22  S: 1/3
+E4M3 - 0:08.86 (0:33)  K:  1/140  I:  1/5   S: 0/22
+E4M4 - 0:32.43 (1:05)  K: 19/60   I: 11/23  S: 0/2
+E4M5 - 0:22.89 (1:27)  K: 10/73   I:  0/29  S: 0/2
+E4M6 - 0:23.66 (1:50)  K: 21/97   I:  0/4   S: 0/3
+E4M7 - 0:09.94 (1:59)  K:  6/97   I:  1/18  S: 0/4
+E4M8 - 0:33.94 (2:32)  K: 39/106  I:  1/6   S: 0/1
+
+
[+]New key binding for warp directly to the next stats screen. <End> by default. +
[!]Option "step of speed change" has been restored. Two modes are available: stepwise and automatic. +
[-]PrBoom dehacked support: wrong processing of Bits parameter if its value is equal to zero. No more desync on HACX demos. +
[-]PrBoom compatibility: PRBoom doesn't spawn the boom-specific "specials" in demo_compatibility mode now (generalized scrollers, sector effects). No more desync on umbrella.wad\um3nm152.lmp. +
[-]PrBoom compatibility: MF_JUSTHIT fix. No more desync on Sam Woodman's HMP Max demos (HX17-459.LMP etc). +
[-]PrBoom: fix for turn-snapping bug on fullscreen in software mode. "..\Advanced HUD settings\Movements\Fix Turn-Snapping issue" +
[-]PrBoom: deathmatch starts as unknown things with a complevel of 0, 1, 2 or 3. +
[-]GlBoom: problems with transfering not flipped sky texture (line type 271) to tagged sectors. On war_3.wad\map14 for example. +
[-]Mistake of processing of the "quick rewind" key if the player dies. +
+ +
2.2.6.21 + +
[+]Added switch to force monster_avoid_hazards: -force_monster_avoid_hazards. It is meaningful for viewing doom-compatible demos recorded with "monster_avoid_hazards 1" in config (in cases of desyncs). +
[+]Renders bsp tree in automap mode. +
[!]Allows mouselook during playdemo in camera mode. +
[!]FOV units have been adjusted to conform with commonly used scale. (64=>90) +
[!]The step of gamespeed change varies automatically depending on current speed. Option "step of speed change" is removed. +
[-]Wrong keys size in automap mode. (the bug was introduced in 2.2.6.19) +
+ +
2.2.6.20 + +
[+]GLBoom: multisamling (anti-aliasing) support. +
[+]GLBoom: "smart items clipping" setting. +
[-]Detail texture: dividing by zero. +
+ +
2.2.6.19 + +
[+]Support up to 65536 sidedefs instead of 32768. +
+ +
2.2.6.18 + +
[+]"Mouse Look" sprite for "Mouse Sensitivity" in menu. +
[+]Demo recording: "overwrite existing" options for overwrite of the existing demo if no savegame presents. +
[*]Vert sens. +
[*]Smooth movement: more smooth movements at low FPS. +
[-]Bugs with no mipmap. +
[-]Smooth turns in demos: wrong direction of a player sight after loadgame request caused from the demo. +
+ +
2.2.6.17 + +
[-]Smooth movement: bugs in algorithm: finally it works perfectly. +
+ +
2.2.6.16 + +
[-]Bugs of the previous version. +
[-]Smooth movement: bugs in algorithm appearing at low framerate. +
+ +
2.2.6.15 + +
[*]Full mouselook: a big increase in FPS if the viewpitch of the player is in the range from -45 to +45 degrees. +
[*]Smooth movement: minor changes. +
+ +
2.2.6.14 + +
[*]Smooth turns in demos: changes in algorithm. Now smoothing occurs in a range [currtick-smoothfactor..currtick] instead of [currtick-smoothfactor..currtick+smoothfactor] +
[-]Smooth turns in demos: wrong direction of a player sight after respawn. +
+ +
2.2.6.13 + +
[-]Smooth turns in demos: bug arising after forcibly changing of the player's viewangle by the engine at use of a saw or a fist. (Grazza) +
+ +
2.2.6.12 + +
[*]Smooth turns in demos: changes in algorithm. Now smoothing occurs in a range [currtick-smoothfactor..currtick+smoothfactor] instead of [currtick-smoothfactor..currtick] +
[-]Smooth turns in demos: turns did not smooth out if the option of smooth movements has been switched off. +
[-]Smooth turns in demos: problems when the demo includes very sharp turns. (Grazza) +
+ +
2.2.6.11 + +
[+]Demos: option for smooth turns during viewing of demos. It makes sense for TAS demos recorded with slowmotion. +
[-]Smooth movement: some flickers after menu closing. +
[-]PRBoom issues: prboom uses monster_avoid_hazards setting from the cfg file when you are using -complevel 0 or -complevel 1. If you have this option set to 1 in your cfg, then you are at risk of getting desyncs in demos that feature crushers. (Dashiva) +
+ +
2.2.6.10 + +
[+]GLBoom: detail texture support for walls and flats. Requires hi-end systems. +
+ +
2.2.6.9 + +
[-]Bugs in the new algorithm of middle textures drawing. +
+ +
2.2.6.8 + +
[*]Not drawing the player gun sprite in the freely rotatable view mode (Janizdreg). +
[-]Run the game with the -nomusic parameter, start a new game (via the menu) and either kill yourself or start a new game again and the music will start playing. (Janizdreg) +
[-]Smooth movement: interpolation did not work for moving platforms after savegame load. +
[-]GLBoom bug: wrong display of a middle texture if it exceeds the boundaries of its floor and ceiling. +
+ +
2.2.6.7 + +
[-]Smooth movement: "I_SignalHandler: Exiting on signal: 11" bug during rendering of the first frames of heavy scenes (after opening doors). +
+ +
2.2.6.6 + +
[-]Smooth movement: doors activated by lines of the generalized types were not interpolated. +
[-]Mouselook & fov: bugs in sky rendering. +
+ +
2.2.6.5 + +
[-]If a new game is started while a demo is playing the walkcam settings are carried over to the single player game. +
+ +
2.2.6.4 + +
[+]Full mouselook and FOV in GLBoom (without compatibility loss). +
[*]Smooth movement: few changes in algorithm. +
+ +
2.2.6.3 + +
[-]Smooth movement: blinking in the second level of the Cchest2.wad. +
[-]Smooth movement: lifts activated by lines of the generalized types were not interpolated. +
+ + diff --git a/doc/prboom-plus-history.txt b/doc/prboom-plus-history.txt new file mode 100644 index 0000000..ff11f41 --- /dev/null +++ b/doc/prboom-plus-history.txt @@ -0,0 +1,918 @@ +Change Log + +2.5.1.4 @ 2016-Jan-10 +[+] Added "Fix clipping problems in large levels" option. +[+] Added "gl_finish" config variable. +[+] Added "mus_fluidsynth_gain" and "mus_opl_gain cfg" config variables to fine tune output of fluidsynth and opl2 midi. Values allowed are 0 to 1000. 50 (default) exactly matches old behavior. +[+] Added a "Health Bar Above Monsters" option (health_bar* config variables). +[+] Added a "Things appearance" automap option. Possible values: "classic", "scaled" and "icons". +[+] Added "notarget" and "fly" cheat codes. +[+] Added MBF's "-beta" codepointers. +[+] Added a new HUD. +[+] Added "shaders" sector light mode. +[+] Support "Classic Doom" WAD files of Doom 3 BFG Edition, by Fabian Greffrath. +[+] Support for HACX 1.2 IWAD, by Fabian Greffrath. +[+] Support up to eight joystick buttons instead of just four, by Fabian Greffrath. The fifth and sixth buttons are mapped to strafe left and right. +[+] Mouse look now is available in software mode. +[+] Added a crosshair. Three different crosshair graphics are for choice: cross, angle and dot. Extra features are changing crosshair colors according to the player's health and/or on sight of a target. +[+] Added "Allow Jump" option on "Prboom-plus 'bad' compatibility settings" page. Implemented by Fabian Greffrath. +[+] Added a "Backpack Changes Thresholds" option. +[+] -skipsec accepts a minutes prefix followed by a colon. +[+] Two-key strafe50: StrafeOn + MoveLR = strafe50 +[+] Added "Allow Vertical Aiming" option on "Prboom-plus 'bad' compatibility settings" page. +[*] Update to newest SDL libraries: SDL_mixer 1.2.12, SDL_image 1.2.12, SDL_net 1.2.8. +[*] Brown color for weapons that cannot be fired on weapon HUD widget. +[*] "Use GL surface for software mode" mode now works much faster if gl_finish is 0 in config. +[*] process_affinity_mask config variable is removed. Single CPU core will be automatically forced if SDL MIDI player is used. +[*] Better support for Chex Quest, by Fabian Greffrath. Embed chex.deh by fraggle in prboom-plus.wad. +[*] Redo MBF-style multiple DEHACKED lumps. Load command line DeHackEd patches after DEHACKED lumps. +[*] Improved rendering precision (wall wiggle), by kb1. +[*] Realign automap grid with blockmap. +[-] 'Max Health', 'Max Soulsphere' and 'Megasphere Health' DEH parameters did not work after warping to level from command line since 2.4.8.1. +[-] Buggy invulnerability effect if hi-res textures are used and there is no support for GL_ARB_framebuffer_object. +[-] Simple shadows flicker during lowering lifts. +[-] "Change palette on pain/bonus/power" settings did not work. +[-] Lines of walls on automap were not displayed on Planisphere 2 at some zoom. +[-] Fixed HOMs on Planisphere 2. +[-] Incorrect clipping of automap in opaque mode after changing view window size. Affects only software mode. +[-] Fixed long wall error. +[-] Boom's ability to change floor and ceiling lighting independently was not applied to things in opengl. + +2.5.1.3 @ 2011-Dec-05 +[+] Added process_priority config variable. 0 - normal (default); 1 - high; 2 - realtime. +[*] "Screen multiple factor" can be changed on the fly. +[-] Fixed desynch on ep3-2349.lmp. +[-] Fixed issues in screenshot and video capturing code. + +2.5.1.2 @ 2011-Nov-25 +[+] Added device selection to portmidi player. Controlled by the snd_mididev config variable. See stdout.txt for list of available devices. +[+] Added a progress bar for demo skipping during re-recording. +[+] Added key binding options for start/stop and fast-forward when watching demos. +[+] Added a key binding option to restart the current map. +[+] Added a "Default compatibility level" GUI entry. +[+] Support for 16 sprite rotations. http://zdoom.org/wiki/Sprite#Angles +[+] New HUDs. HUDs definitions are moved to the "prboom-plus.wad/-prbhud-" lump. +[*] Fluidsynth player now resumes notes seamlessly after a pause. +[*] Speed improvement on maps like sunder.wad map10 and nuts.wad. +[*] Force GL_LINEAR for MAG filter for textures with detail. +[-] Fixed buggy music that forgets to terminate notes held over a loop point. SDL_mixer does this as well. Fix tested against Doom2 map14 and FreeDoom2 map01, for fluidsynth, portmidi, and OPL players. +[-] Fixed buffer overrun in OPL2 player. +[-] Fixed crash in video capture code when "Use GL surface for software mode" is enabled. +[-] The screen wipe after pressing the exit switch on a level was noticably jerkier. +[-] MBF-added codepointers worked with any complevel. +[-] Fixed HOMs on skies with transparent pixels. Sky2 @ doom2.wad, for example. +[-] Alpha channel did not work for 8-bit PNGs with alpha. +[-] Transferred standard sky was drawn badly when FOV > 90 (Voodoo Guns - map02). + +2.5.1.1 @ 2011-Jun-18 +[+] Support for ZDoom-style OGG loop points, by Nicholai Main. http://zdoom.org/wiki/Audio_loop +[+] Ability to disable the background on Boom fullscreen menus. There is a "menu_background" config variable and in-game GUI entry. +[*] "Automap - Show kills/items/secrets statistics" is displayed on overlay automap too. +[*] Improved sample rate conversion, by Nicholai Main. +[*] MIDI volume works independently from SFX on Vista/7 for "portmidi" player. +[*] "madplayer" should now correctly play mp3 songs with ID3v2 tags. +[*] Seamless tiling in the high quality resize algorithms (thanks Nunya). +[*] The first frame of the O_COUNTD(.MP3) @ dvii-1u.wad is in fact corrupt, but the rest is ok (best guess: it used to have an ID3v2 tag which was removed by a dodgy program). The new load routine should be more accepting of damaged mp3s. +[-] "Texture format" always forced to GL_RGB5_A1 if "Use GL surface for software mode" is enabled. +[-] Fixed crash when you try to make a screenshot if "Use GL surface for software mode" is enabled. +[-] Deadlock in "portmidi" music player. +[-] Looping for all mp3s are completely broken in libmad player. +[-] Fixed playback of mono MP3 files with libmad player. + +2.5.1.0 @ 2011-May-28 +[+] New music code by Nicholai Main. See "Preferred MIDI player" GUI option. Available values are "sdl", "fluidsynth" (disabled for Win9x), "opl2" and "portmidi". +[+] If "-warp" doesn't have a map number after it, the game will automatically warp to the first map in all the files loaded at the command line. This allows a pwad to be run without concerns about where its actual maps start. +[+] The option to place the overlaid automap where you want (map_overlay_pos_x/y/width/height config variables). +[+] Added render_patches_scalex/y config variables for custom scaling when "not adjusted" is used. +[+] Emulation of weaponinfo overruns. +[+] Automap interpolation. +[+] Support for mouse wheel up/down. +[+] Added an "Use mouse wheel for zooming" automap option. +[+] Screen resolution changes don't require an engine restart. +[+] The option to resize the main window with the mouse. Any size is allowed if Shift is pressed during resizing. +[+] Alt-Enter now toggles between fullscreen and windowed modes. +[+] Added an "Use GL surface for software mode" video option. It renders a software surface into an OpenGL surface. Thus, you shouldn't have palette issues on modern systems and you can use VSync even with the "windib" SDL video driver. +[+] New video capture system by Nicholai Main. Use command line params "-timedemo anydemo.lmp -viddump filename.mkv"; see usage.txt for more details. +[*] Added mouse button actions for "backward motion" and for single-click "use". +[*] The noclip effect which can occur with an "intercepts overflow" should not take effect after a level is reloaded. +[*] When recording, the ENDOOM screen is now disabled regardless of the misc_fastexit option. +[*] The "Fast Exit" option is now named "Show ENDOOM screen". +[*] The precise rendering of automap lines when "render quality" is "quality" to avoid small vibrations during map rotation. Applied only in GL mode. +[*] Antialiasing of automap lines now also works in software modes (Xiaolin Wu's line algorithm). There's a map_use_multisamling config variable and a corresponding GUI entry for this. +[-] Fixed a crash on map21 @ newgothic.wad with "-complevel 10" and higher. +[-] Improved emulation of "missed back side" overruns. The desynch in fez1-924.lmp @ fez1.wad is gone now, but you still need to add "-setmem dosbox" or "-setmem dos71" command line parameter, because the default "dos622" memory layout causes a desynch. +[-] The automap scaling factor is now saved. +[-] A "-set overrun_* = 0" setting now works in demo footers. +[-] When playing back multi-level demos for the Ultimate DOOM, -warp produced unexpected results when combined with -auto. +[-] The titlepic in malgnant.wad caused Signal 11 crashes. +[-] Fixed the incorrect positioning of automap marks when 'follow mode' and 'rotate mode' were enabled. +[-] Automap marks did not draw correctly, displaying white rectangles instead of digits. +[-] Automap marks did not scale to the selected screen resolution and were hardly visible at high resolutions as a result. +[-] The skydome cap color was wrong during invulnerability mode. +[-] The mouse cursor didn't hide during fullscreen when the mouse was disabled in General options. +[-] When one used skipsec to warp to an intermission screen portion of a demo, the music from the previous level was played instead of the intermission music. +[-] The "flashing hom" detector didn't work in GL mode. +Links for the demos, wads and topics referred to above: +http://www.doomworld.com/idgames/index.php?id=16238 (newgothic.wad) +https://dsdarchive.com/wads/fez1 (fez1-924.lmp, fez1.wad) +http://www.doomworld.com/idgames/index.php?id=1509 (malgnant.wad) + +2.5.0.8 @ 2010-Nov-13 +[+] Added support for textured automap. Available in the menu at "Options\Setup\Automap\Enable Textured Display". +[+] "Next Level" key now works during normal play, and not just during demo playback. +[+] Added "-shotdir" command line switch and "screenshot_dir" config variable. Default folder for screenshots is now exe_dir. +[+] Two new sponsored HUDs. +[*] Added ability (overrun_missedbackside_emulate 1) to emulate wrong memory access for cases when some linedefs have a two-sided flag set, but no second sidedef. Fixed desynch on CLNJ-506.LMP @ CHALLENJ.WAD +[*] Broken BLOCKMAP lumps won't be recreated automatically. Use "-blockmap" command line switch if PrBoom-plus reports a buggy blockmap. +[*] The "-solo-net" and "-emulate x.y.z" params are saved to the demo footer. +[*] Added frustum culling for sprites if mouse look is enabled (gl_sprites_frustum_culling). If you look up or down on nuts.wad with this, frame rate will increase instead of decreasing. +[*] Improved compatibility with Doom 1.2. All known Doom 1.2 demos including ret12na.lmp @ return01.wad, wp2-33.lmp @ wadpak2.wad and uac_dead by Never_Again are now in sync. +[*] GLBDEFS alias for GLDEFS lump to avoid compatibility issues with other ports. Note that if GLBDEFS is present, GLDEFS will not be parsed at all. +[*] Command-line added demos don't conflict with lump names anymore. For example, you can play map01.lmp back on map01. +[*] Incorrect nodes on e1m1 @ deepcre.wad don't crash PrBoom-plus anymore. +[-] The title screen from the Duke Nukem total conversion PWAD didn't appear in software mode, being replaced by a black screen. +[-] Pitch-shift emulation didn't work. +[-] On-screen graphics like PAUSE, M_DOOM, TITLEPIC, etc didn't fit in the screen at 16x9 aspect ratios and "not adjusted" mode for "status bar and menu appearance". +[-] Fixed glitch in "no texture" mapping trick under invulnerability when using hires textures. + +Links for the demos, wads and topics referred to above: +https://dsdarchive.com/wads/challenj (CLNJ-506.LMP, CHALLENJ.WAD) +http://www.doomwadstation.com/main/dukegp.rar (Duke Nukem total conversion) +https://dsdarchive.com/wads/return01 (return01.wad, ret12na.lmp) +https://dsdarchive.com/wads/wadpak2 (wadpak2.wad, wp2-33.lmp) +https://dsdarchive.com/wads/uac_dead (uac_dead.wad and doom 1.2 demos) +http://www.doomworld.com/idgames/index.php?id=4169 (deepcre.wad) + +2.5.0.7 @ 2010-Aug-15 +[+] Added support for DeePBSP v4 extended nodes. +[+] Added support for ZDoom's uncompressed extended nodes. +[+] Animation blending. (Options \ General \ Texture Options \ Blend Animations) +[+] Support for Quake2/Unreal style skyboxes. Their definitions (using GLDEFS lump) are GZDoom compatible. (http://zdoom.org/wiki/GLDEFS#Skybox_definitions) +[+] Support for MUSINFO lump (dynamic music changing) from Risen3D. (http://zdoom.org/wiki/MUSINFO) +[+] Support for custom per-texture detail textures. Syntax of definition using GLDEFS lump: + detail + { + (walls | flats) [default_detail_name [width [height [offset_x [offset_y]]]]] + { + texture_name [detail_name [width [height [offset_x [offset_y]]]]] + } + } + + Where detail_name is a bmp/png/tga/jpg/pcx/gif lump between the HI_START/HI_END markers. You don't need to add the texture to TEXTURES1. Default values are (width:16 height:16 offset_x:0 offset_y:0) + + Example: + + detail + { + walls smooth01 32.0 //default detail for walls (width = 32, height = 16, offset_x/y = 0) + { + brick7 detstone 64.0 64 10.532 + brick8 detail02 // detail02 16 16 0 0 + water1 // do not apply default detail to water + water2 + water3 + water4 + } + flats // no default detail for flats + { + grass1 Grass01 32 32 + NUKAGE1 detslime 16 16 0 0 // different offsets for animated flats make sense + NUKAGE2 detslime 16 16 4 4 + NUKAGE3 detslime 16 16 8 8 + } + } +[*] Hacked SDL_mixer.dll to avoid music bugs on some tracks. (for example: music goes wrong from 1:15 with timidity on doom2.wad map05) +[*] Recalculates seg offsets that are sometimes incorrect with certain nodebuilders. Fixes among others, line 20365 of DV.wad, map 5. Makes sense only for software renderer. +[*] Scan forward for a MUS header signature at the beginning of a MUS lump, which apparently DMX was capable of doing vis-a-vis DIESCUM.WAD. +[*] Verifies blockmap validity and reports it in stdout.txt. +[*] Disables sky texture scaling if the status bar is used. +[*] In Chex Quest, uses the radiation suit colormap instead of the red colormaps that are usually used when taking damage (or getting the berserk pack). This matches Vanilla chex.exe behavior (from chocolate-doom). +[*] Make -complevel 9 draw the sky normally during invulnerability. This is what Boom does. It was MBF that introduced the "negative" sky option. +[*] No z-fighting in OpenGL mode for a killed mobj and its dropped weapon after death even with "sprites_doom_order 1". +[*] The ability to render flats using draw lists. Can be noticeably faster on complex levels. "gl_use_display_lists" in config. +[-] Fixed HOM at sectors 226 and 300 on E1M1 @ OTTAWAU.WAD. Fix affects only vanilla compatibility levels, because Boom's behavior is different. Newtechn.wad is also fixed. +[-] Fixed a -trace_thingshealth issue. +[-] Fixed a crash when demo footer and re-record are used. +[-] Full path should not be stored in demo footer for Dehacked patches. +[-] SDL_mixer's native MIDI music playing does not pause properly. As a workaround, it sets the volume to 0 when paused and "mus_pause_opt 1". +[-] Boom colormaps are not be applied twice for hires textures anymore. +[-] Fixed the latest known incompatibility with MBF. +[-] Fixed an incompatibility with Win95. +[-] During demo playback when using the camera the sound was heard from the players position, instead of the camera position. +[-] After joining a game using recordfromto it was not possible to save the game. +[-] Inheriting of friendlyness during spawn, for MBF compatibility. +[-] PrBoom did not use the misc1 and misc2 frame parameters in A_Mushroom for Boom compatibility. Normally those two should control the angle and speed factor of the fireballs, but it didn't happen here. +[-] "map_use_multisamling 0" did not work with "map_always_updates 0" +[-] Fixed crash in software mode with 5x4 aspect. +[-] Fixed mismatches between sotware and GL modes at 5x4 aspect. +[-] Walkcam did not interpolate when paused. +[-] Color variables were misaligned in the automap settings menu with "not adjusted" format. +[-] A string of garbage from the title picture was left on the bottom of the HUD when using "not adjusted" format at resolutions with odd height (700x525 as example). +[-] Fixed corruption of graphics after smoothing edges on systems with LP64 data model. Most Unix (like Solaris) and Unix-like systems (like Linux). + +2.5.0.6 @ 2009.12.27 +[+] Added the ability to smooth sprites edges ("Options\General\OpenGL Options\Smooth Sprite Edges"). Might noticeably reduce frame rate. Includes a "gl_mask_sprite_threshold" config variable for the intensity of smoothing (0-100). +[+] Ability to see kills/secrets/items/time statistics in automap mode. Available in the menu at "Options\Setup\Automap\". +[+] Added a "map_use_multisamling" config variable to apply multisampling to the automap. +[+] Added a "movement_shorttics" config variable in addition to the "-shorttics" command line switch. +[+] Added a "gl_shadows_maxdist" config variable. +[+] Added the ability to disable any OpenGL extension via the config instead of disabling all of them with "gl_compatibility" in case your drivers can't work properly with specific features. The extensions which can be disabled are: gl_arb_multitexture, gl_arb_texture_compression, gl_arb_texture_non_power_of_two, gl_ext_arb_vertex_buffer_object, gl_ext_blend_color, gl_ext_framebuffer_object, gl_ext_packed_depth_stencil, gl_ext_texture_filter_anisotropic, gl_use_stencil. +[+] Added a "-trace_givendamage" command line parameter to trace the latest and total damage inflicted by any specific monsters or players in a level. If a player is selected, its data will be tracked across multiple levels. Available with the Boom HUD only. Usage: + -trace_givendamage ThingID [ThingID] [ThingID] +[+] Added a "map_scroll_speed" cfg variable and GUI setting. "key_speed" doubles the speed. +[+] Added a "sprites_doom_order" config variable for correct sprite sorting. See sectors 99, 115, and so on in Strain.wad map13, for example. This also fixes z-fighting between overlapped sprites in GL mode. +[*] Mouselook key and keys bound to game speed change commands do not break cheats being typed. +[*] Midi volume is applied to all midi devices when mus_extend_volume is 1. +[*] Light level 0 will now be truly lightless with GZDoom lighting mode. +[*] Automap grid can now rotate with map (from ZDoom). +[*] Now DSSECRET is used when a secret area is found, instead of DSITMBK. The new sound lump is part of prboom-plus.wad and can be replaced through PWADs. +[-] Fixed a crash in software mode at some resolutions when using 'Doom Format' or 'Fit to Width' for 'Status Bar and Menu Appearance'. +[-] Now applies fake contrast when using "Fog Based" sector light mode. +[-] Fixed Player's weapon lighting, that was too dark in GL mode. +[-] Fixed small glitches on Boom HUD graphics in GL mode. +[-] Mouse button now moves camera forward if you have it assigned to move forward. +[-] Fixed software mode bug: if you played a wad with menu graphics big enough to go over the status bar, entering and exiting the menu left a trace of the menu graphic over the status bar. See HR2final.wad, for example. +[-] Flat bleeding on walls won't incorrectly bleed through walls and floors when motion blur is in effect anymore. This fix requires support of the "GL_EXT_packed_depth_stencil" extension. +[-] Simple shadows and transparent textures don't sometimes flicker anymore when fog-based lighting is used. +[-] Shadows don't disappear in some situations anymore. +[-] "-trace_thingshealth 0" now works. +[-] Fixed screen shots at resolutions where width is not a multiple of 4. +[-] Fixed slowdowns on Pentium 4 at 1024x768 (introduced in 2.5.0.3). + +2.5.0.5 @ 2009.11.14 +[+] Added an "sdl_video_window_pos" config variable. Syntax: + sdl_video_window_pos "center" + sdl_video_window_pos "100,100" + sdl_video_window_pos "" +[+] Access violation emulation for the "S1 Floor Raise Donut (changes texture) (9)" action in demo compatibility mode. "-donut floorheight floorpic" - takes custom values, overriding the program's default values ("-donut 0xF1000000 7", for DOSBox behavior). Disabled by default. +[*] Updated to newest SDL libraries: SDL.dll v1.2.14, SDL_net.dll v1.2.9. +[*] The "Sky Mode" GUI setting and "gl_drawskys" config variable have been removed. Now "SkyBox" is used if mouse look is enabled, "Standard Sky" otherwise. There is no more sky stretching with "SkyBox" mode. +[-] Fixed stat screen animation for The Ultimate Doom and the Doom II cast BOSSBACK after MAP30 when using 'not adjusted' rendering. +[-] Fixed detail walls when "GL_ARB_multitexture" extension is not supported or gl_compatibility is used. +[-] Fixed bug where music doesn't change after IDMUS is used and a New Game is started. +[-] Fixed bug where using the "End Level" key while a demo is paused breaks the "End Level" feature. +[-] Fixed vertical scrolling of sky texture in SkyBox mode. +[-] Now the mirror flag for MBF skies is used in SkyBox mode. +[-] Fixed music playback on Windows Vista and 7. + +2.5.0.4 @ 2009.10.07 +[+] Full support for wide resolutions. Aspect will be detected automaticaly, but you can force any ratio from the GUI. +[+] Options\General\Status Bar and Menu Appearance: Not Adjusted / Doom Format / Fit to Width. +[+] Added a -nocheats command line parameter and a deh_apply_cheats config variable to disable applying cheats from dehacked files. +[+] Added a key binding option for mouse look. +[+] Added a "map_grid_size" config variable. +[+] Added a "linear mipmap" texture filtering mode. +[*] Less aggressive patch correctness checks. Makes sense for OBITUARY, where for some patches there is no column beginning exactly at the end of the column directory. +[*] More permissive cheats handler for non-Boom complevels. The following DEH cheats now work: + + No Clipping 1 = !@#$%^&*() - you can't move in Boom with such a cheat + No Clipping 1 = bb - works incorrectly in Boom + No Clipping 1 = b/b - does not work at all in Boom, but works in vanilla + + Making a short sequence on a cheat with parameters (idclevXX) will not work in vanilla (0-6) and lxdoom (10) complevels, as in the respective engines. +[*] Ignores sprite lumps smaller than 8 bytes (the smallest possible) in size - this was used by some dmadds wads to mark 'empty' graphic resources. Now you can play 22ventry.wad. +[*] The Boom DEH parser has been restored. You can force it only for demo playback with -boom_deh_parser command line switch. +[-] Fixed a crash on wads with incomplete REJECT table. (introduced in 2.5.0.2) +[-] Fixed an incompatibility with Boom if you have player_bobbing 0 in cfg. (introduced in PrBoom 2.2.2) As a result, there is now a desynch on sewv12r-2135.lmp. Use -emulate 2.2.2 .. 2.5.0.2 for the old buggy behavior when playing such demos. +[-] Fixed a crash related to A_BFGSpray with NULL target when using dehacked patches - discovered with insaned2.deh (thanks CSonicGo and fraggle). 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 it lacked proper memory protection. +[-] Fixed a crash (division by zero) in Boom HUD code for "Max ammo = 0" in a DEH. +[-] Fixed flats drawing on a Mac in case of render_precise 1. +[-] Fixed an overflow of the mapnames array (-iwad tnt.wad -file ht.wad -warp 33) +[-] Avoids z-fighting between grid and walls on automap in GL mode. (introduced in 2.5.0.3) +[-] Fixed a vanilla automap grid bug: losing grid lines near the map boundary. (from EE) +[-] Fixed an issue where walls look full-bright from close up. Added a "render_old_lightmaps" config variable for 16 (old) or 32 (new) lighting levels. +[-] Fixed an issue with fake floors. See the colored room in boomedit.wad, as an example. (introduced in 2.5.0.2) +[-] Hi-res textures in TGA format are now loaded from PWADs. + +Links for the demos, wads and topics referred to above: +http://www.doomworld.com/vb/showthread.php?s=&postid=790596#post790596 (sewv12r-2135.lmp) +http://www.doomworld.com/idgames/index.php?id=4442 (insane2.deh) +http://www.doomworld.com/idgames/index.php?id=2849 (22ventry.wad) + +2.5.0.3 @ 2009.07.11 +[+] Simple shadows, from Doomsday. +[+] New "Fog Based" sector light mode which is very similar to software mode. If it doesn't work correctly, try updating your drivers or replacing your ATI, because per-pixel fog (GL_NICEST) doesn't work with some ATI cards or their drivers. +[+] New map_always_updates config variable that can be toggled between updating unexplored parts in automap mode or the classic behavior. +[*] No multisampling in automap mode. +[*] Speed improvement in OpenGL mode: ~25% on nuts.wad, 7% generally. +[*] Automap drawing optimisation in GL mode: 220 fps instead of only 30 on map05 @ dv.wad after IDDT IDDT. +[-] Fixed middle texture bug on Claustrophobia 1024 map06, sector 39. +[-] Fixed desynch on longdays.wad (-emulate 2.5.0.1/2 for viewing demos recorded with buggy versions). + +2.5.0.2 @ 2009.05.09 +[+] Support for DeePsea's tall patch method: Now it's possible to use patches of any height. +[+] Support for hi-res textures in wads. You should place them between HI_START/HI_END markers. +[+] Fog in "gzdoom" lighting mode. +[+] High Quality Resize modes. Scale2x/3x/4x are supported. Thanks to Benjamin Berkels. +[+] An alternative way of sky drawing was added. "Sky Mode: Skybox". It's slower than the "Standard" method, but looks better if you use mouse look. Also, it's the only way to see skies properly on old hardware, like 3Dfx Voodoo Graphics, ATI 3D Rage Pro, and others which don't support "Environment Mapping". +[+] Added a "gl_clear" config variable for clearing the screen between frames. This is only necessary when developing a map, to see HOMs. +[+] Added a "gl_ztrick" config variable for very old hardware, like 3Dfx Voodoo, NVidia RIVA128, etc. If this toggle is enabled the game will not clear the z-buffer between frames. This will result in improved performance (~10%) but might cause problems for some display hardware. Makes sense with gl_compatibility 1 and gl_clear 0. Do not use it with TNT2 or better, as performance will be slower. +[+] Now PrBoom-Plus can save all necessary playback information at the end of demos without loss of compatibility. With such demos you just need to type "prboom-plus demoname" or click on a demo from your browser, archives manager or explorer and prboom will make any additional work by itself without any assistance. PrBoom-Plus will check for a demo footer and if it is present, will read which IWAD, PWAD and DEH files were used for recording, will try to download necessary add-on files from the Internet and will play the demo for you. + Currently, PrBoom-Plus saves the following information (with demo_extendedformat 1): + - VERSION - version of DemoEx format (1.0) + - PORTNAME - port name and version (PrBoom-Plus 2.5.0.1) + - PARAMS - iwad, pwads, dehs and critical for demos params like -spechit + - MLOOK - mouse look data + For the automatic downloading of files, one must specify the getwad_cmdline parameter in the PrBoom-Plus config file. Examples: + getwad_cmdline "D:\games\Doom2\getwad\getwad.exe" + getwad_cmdline "C:\my_cool_app -download_pwad_from_somewhere %wadname% -here c:\doom2" + +[+] PrBoom-Plus can now apply Boom colormaps to hires resources. +[+] Supports independent OpenGL filtering methods (nearest, linear, etc) for textures, sprites and patches. +[+] It's now possible to change screen resolution from the GUI. +[+] An option to see any monsters that are still alive through walls. By default it's toggled with "Numpad /". It has two modes: All sprites with living monsters or living monsters only. Works only in GL mode. +[*] PrBoom will attempt to play a DEMO4 lump present in a wad immediately after DEMO3, but without exiting with the "W_GetNumForName: DEMO4 not found" error that the Plutonia and TNT executables get. This makes sense for Plutonia 2, which includes a DEMO4 lump that is played back during the demo cycle by vanilla Plutonia. +[*] "PrBoom-Plus" title instead of "PrBoom" on the CREDITS screen page. +[*] Doesn't alter the default skill level when the difficulty skill is selected from the New game menu. An explicit default skill option was added instead, shoehorned into the misc section of page 2 of the General menu. +[*] Smoothing for the edges of textures with holes. (And not only for sprites, as before.) +[*] Screenshots in "gzdoom" lighting mode use current gamma. +[*] Reconfiguration in menu. +[*] Better and faster mipmapping for modern hardware. Support for GL_ARB_TEXTURE_NON_POWER_OF_TWO extention. +[*] Two service variables for developers were added: gl_color_mip_levels and gl_fog_color. +[*] PrBoom-Plus doesn't use detail texture over hires textures. +[*] Textures are reloaded after changing the "Override PWAD's graphics with Hi-Res" option. +[*] Smart algorithm for more seamless flats drawing. In some situations this removes seams from hires flats (between sectors 125 and 126 on map20 @ doom2.wad, etc). +[*] Speed improvement in OpenGL mode. +[*] Can play any episode with The Ultimate Doom, Final Doom, Doom95 and DosDoom compatibilities and -warp command line switch. Now you can play E5M1 @ 2002ado.wad with "-warp 5 1 -complevel 3". The (vanilla) Ultimate Doom executable also allows this. +[*] Some interpolation code was optimized. This fixes slowdowns on the beta of ndcp2.wad map02. +[*] Speed improvements. Some timedemo examples for my hardware (Intel Core2Duo 3.0, NVidia GeForce 8800 GTS): + + Command line: [port] -geom 640x480 -window -nosound -file [pwad] -timedemo [demo] + Files: epic.wad, epic.lmp, nuts.wad, nuts.lmp, dv.wad + + epic.wad nuts.wad dv.wad + glboom 2.5.0 126 53 560 + glboom+ 2.5.0.1 234 130 640 + glboom 2.5.0.1.fast 277 134 705 + glboom+ 2.5.0.2 362 149 834 + + prboom 2.5.0 174 115 400 + prboom+ 2.5.0.1 158 114 382 + prboom+ 2.5.0.2 191 121 424 + +[-] Fixed a rare wrong sprite rotation glitch when render_paperitems is 0. +[-] Avoids segfaults on levels without nodes, and refuses to load maps with an incomplete pwad structure. +[-] Avoids segfaults with unknown patch formats, and checks if the patch lump can be a Doom patch. +[-] Gets rid of a line of graphics on some middle textures with holes. Sector #42 on gravity.wad as an example. +[-] Doesn't render holes between flats and walls in some situations, on some hardware. Sector #557 @ map21 @ pl2.wad. GLBoom-Plus now renders per linedef instead of per seg. Performance has increased a little as a bonus. Also, some small texturing glitches are gone. See linedef #361 @ bbb.wad, as an example. +[-] Gets rid of a half-pixel mismatch on seams between identical and correctly aligned textures in GL. This was an old GLBoom bug which is now fixed. +[-] Fixed incorrect flipping for "Transfer Sky Texture" actions (272 <-> 271) in OpenGL. +[-] Fixed issue with near-clip plane. Works only on NVIDIA. + +From PrBoom +- Don't set default skill level from the new game menu. Added explicit default skill option. + +Links for the demos, wads and topics referred to above: +http://www.doomworld.com/idgames/index.php?id=15467 (gravity.wad) +http://www.doomworld.com/idgames/index.php?id=15216 (bbb.wad) +http://www.doomworld.com/idgames/index.php?id=15125 (epic.wad) +http://prboom-plus.sourceforge.net/epic.zip (epic.lmp) +http://www.doomworld.com/idgames/index.php?id=11402 (nuts.wad) +http://prboom-plus.sourceforge.net/nuts.zip (nuts.lmp) +http://www.doomworld.com/idgames/index.php?id=12597 (dv.wad) +http://www.doomworld.com/idgames/index.php?id=15550 (pl2.wad) +http://www.doomworld.com/idgames/index.php?id=15652 (ndcp2.wad) +http://www.doomworld.com/idgames/index.php?id=11657 (2002ado.wad) + +2.5.0.1 @ 2008.11.23 +[!] Updated to the latest prboom 2.4.8. (SVN build, revision 2936) +[+] New hardware-mode algorithm for drawing flats into holes left by missing textures, greatly improving compatibility with software mode. This requires a stencil. This revision fixes at least one billion HOMs on various levels which use this software trick. Eternal.wad map28 as example. Old algo will be used with gl_compatibility 1 in cfg. Thanks to GrafZahl. +[+] Experimental cygwin/mingw32 compilation support. +[*] Resolution limitation is removed. Memory usage has decreased as a bonus. 7 mb instead of 8 on map01 @ doom2.wad and 14 mb instead of 18 on map05 @ epic.wad at 640x480. +[*] launcher_enable variable now has three values: "never", "smart" and "always". "Smart" behaviour was default earlier. +[*] Attempt to optimise screen pitch to reduce CPU cache misses in any resolution. +[*] Do precache level with normal demo playback if level_precache 1 +[-] Fixed rare crash in FixedDiv introduced in Boom. +[-] Status Bar was affected by light level of the sector on some on-board Intel video cards. +[-] PrBoom will not try to show (with following unexpected exit) the fourth episode menu item for pre-ultimate iwads. Makes sense for doom.wad v1.2, DOOM 1 shareware and DOOM 1 registered. (introduced in 2.4.8.3) +[-] Re-fix vanilla imprecise calculation (vibrations) of the texture coordinates for flats in software renderer. +[-] Fixed wipe in software. + +From PrBoom +- Restore special case for trivial maps (bug #1837422) +- Fix linear filtering on flats in software mode +- Fix crash when an unknown sprite is used with a non-zero frame number +- Fix crash by testing for null targets in mancubus fire code pointers + +2.4.8.5 @ 2008.10.02 +[*] Set processor affinity mask under non-Windows platforms using the POSIX API (from chocolate-doom). This is a workaround for a bug in SDL_mixer that causes occasional crashes. +[-] Fixed crash when the nodes used for in-wad demo playback are not the same as those used for playing. (introduced in 2.4.8.3) +[-] Strange behaviour may result if a dehacked patch changed the music or sound lump names. (introduced in 2.4.8.2) +[-] Fixed crash if a response file (i.e. @myargs.rsm) is used. + +2.4.8.4 @ 2008.09.07 +[*] New application icon. +[*] ENDOOM/ENDBOOM support using text mode emulation by fraggle. +[*] New algorithm for detection of fake flats and ceilings. It is much more correct and little bit quicker. Now glboom+ can detect complex systems from sectors without bottom/top textures and draw correct height, lighting and pic. Eternal.wad, map05, sectors 462 and 489 are simple examples; see sourceforge bug #2083354 for more examples. +[-] Memory overrun in F_BunnyScroll in software mode. Introduced in 2.4.8.3. +[-] Wrong flats drawing in software mode. Introduced in 2.4.8.3. + +2.4.8.3 @ 2008.08.23 +[!] Updated to the latest prboom 2.4.8. (SVN build, revision 2741) +[+] Support for DDS format of hires textures. +[+] Ability to change the level of anisotropic filtering from the in-game menu. +[+] Real "black and white" effect for invul. It needs OpenGL 1.3. +[+] Motion blur effect for strafe50. (gl_motionblur 1 in config) +[+] Ability to use only the allowed CPUs. It is necessary for buggy SDL_mixer (<=1.2.8 at least) on multi-processors at least win32 systems. process_affinity_mask in config. A process affinity mask is a bit vector in which each bit represents the processor on which the threads of the process are allowed to run. +[+] gl_compatibility variable in cfg. Try setting it to 1 if you have any problems in OpenGL. All OpenGL extensions will be disabled in this case. +[+] An alternative way of sky drawing was added (gl_drawskys 2 in cfg). This method make sense only for old hardware which has no support for GL_TEXTURE_GEN_*. Voodoo, for example. There are some bugs with alternative skies (see map12), but it is better than being without a sky at all. If you have red color instead of sky, then this ability is for you. +[+] Ability to play wads with wrong SEGS lump if workaround is possible. Demo recording is not allowed on such levels. See e1m9 @ NIVELES.WAD as example. +[+] Additional sector light mode was added. (Options\General\Sector Light Mode - GZDOOM) This method has 32 levels of brightness instead of 5. There is new command-line switch "-resetgamma" for restoring original gamma after crashes. +[+] Ability to play MP3 and OGG from wad. +[+] New 'mixed' lighting mode for opengl. It uses gamma ramps like in gzdoom mode and it has 32 levels of gamma, but instead of changing device gamma it applies new gamma to textures. So it needs to reload all textures after change of gamma. +[+] Added demo compatibility with chex.exe ("-exe chex" command-line switch) + glboom-plus.exe -iwad doom.wad -exe chex -file chex.wad -deh chex.deh -playdemo chex1.lmp + You must use chex.deh by fraggle + http://chexmania.tripod.com/downloads.htm + http://www.doomworld.com/idgames/?id=15420 +[*] Win32 build is compiled with Microsoft Visual Studio 2008. +[*] OpenGL: Original doom added/removed one light level for walls exactly vertical/horizontal on a map, but old code used 8 instead of 16. +[*] More precise rotation of sprites if render_paperitems is set to zero. +[*] Unsupported GL nodes will be ignored instead of closing PrBoom-Plus with an error message. +[*] Ability to play wads with wrong flat names. Unknown flats will be replaced with "NO TEXTURE" preset from prboom-plus.wad. +[*] Make missing sounds non-fatal. Makes sense for doom.wad v1.2 to skip some absent sounds. +[*] Intercepts overrun code should work properly on big endian machines as well as little endian machines. +[*] The title screen music in deca.wad is a corrupted mus that nonetheless somehow manages to play properly in Vanilla Doom. Now you can hear such music in PrBoom-Plus too, because new mus2mid code is capable of converting the corrupted mus when MUS header checking is disabled. +[*] MBF Sky property-transfer linedef types will be applied for boom compatibility again. It was a bad idea to disable it. +[*] Advanced syntax for -geom command-line switch. Syntax: -geom WidthxHeight[w|f], w - windowed, f - fullscreen. Examples: -geom 320x200f, -geom 640x480w, -geom 1024x768 +[*] Speedup in software renderer. +[*] "Rendering Quality" now is available in software mode too. "Quality" mode can kill some "slime trails", but it works slower. In the previous version of PrBoom-Plus the "Quality" mode was forced. +[*] Lower memory usage if colormaps are used in OpenGL. +[*] Speedup of level reloading in OpenGL mode (savegame, loadgame, loadgame...). Loadgame from the current (same) level now is much faster (100x). It is very noticeable on big levels like MAP05 @ epic.wad. +[*] There is no longer a need to restart glboom-plus after a change of texture filter (mipmap, aniso) +[*] More accurate patch drawing. Predefined arrays are used instead of dynamic calculation of the top and bottom screen coordinates of a column. New algo makes sense for small elements of the HUD (digits) in resolutions which are not multiple of 320x200. +[*] "-videodriver" switch choice is saved in cfg. +[*] For pre-Ultimate Doom complevels, the fourth episode is not allowed. +[-] Fixed crash after "reset to defaults" from in-game menu. It's an old bug and all the previous versions of prboom worked without crash only by luck. +[-] Fixed bug if translucency percentage is less than 50, in which case all translucent textures and sprites disappeared completely. +[-] Disabling transparency in OpenGL for original sprites which are not changed by dehacked, because it does not work correctly in cases when transparent sprites are behind transparent walls. Global sorting of transparent sprites and walls is needed. +[-] Mouse look did not work on automap in overlay mode. +[-] Fix bug with bringing up the launcher with SHIFT key. +[-] Rooms you haven't visited and are behind closed doors (with no windows) show up on the automap when you walk up to the doors without opening them. This bug was introduced in the 2.4.8.2 release with new clipper code. +[-] Fixed problems with association of PrBoom-Plus with DOOM demos if you are not Administrator. Makes sense for Vista. +[-] Fix bug with sky at the bottom part of an imaginary skybox around a level if mouse look is used. Map30 @ "Deus Vult II: First Edition" is a good example. +[-] OpenGL: More correct (similar to software) drawing of skies with height more than 128 if mouse look is not used. +[-] Fix random color of pixels instead of black during first wiping on fullscreen in software mode. +[-] Bug with sprites on big-endian systems like Apple's Macintosh line prior to the Intel switch. +[-] Fixed error in OpenGL when upper texture on the linedef with "Transfer Sky Texture" action is not set ('-'). 'ASHWALL1' with zero index must be forced in this case, which I did for normal not transferred skies some time ago. +[-] Fix vanilla imprecise calculation (vibrations) of the texture coordinates for flats in software renderer. Visplanes with the same texture now match up far better than before. You can see this bug on cchest2.wad\map02 in the room with moving illumination around the room with sector 265. The new algo is ~1% slower than the original and will be used only if render_precise is 1. +[-] Fixed wrong processing of the "Blue Armor Class" and "Green Armor Class" strings from a DEH. +[-] "Flashing HOM indicator" option did nothing. Fixed. +[-] Fixed crash between e1m4 and e1m5 on chex.wad. +[-] Key for entering setup menu had no entry in config. +[-] The supersecret "COOL!" area of Kama Sutra map15 had a rather ugly texturing bug (GL only). +[-] There is no longer a line of graphics under certain weapons in GL. + +From PrBoom +- Added high color rendering. +- Ignore switches that reference unknown textures instead of exiting. +- When using spy mode in a multiplayer demo in demo_compatibility mode, play the expected player's pickup sounds (the currently displayed player, not the player at the console when the demo was recorded). +- Fix crash when loading maps with missing player starts. +- Fix crash when reloading network savegames. +- Fix bug in transparency rendering caused by doing it in two places. +- Fix the colour of player 4 (red) in multiplayer. +- Fix position of netgame player arrows on automap in rotate mode. +- Always draw player arrows on the automap in deathmatch demo playback. Previously they'd only be drawn if you played the demo from the command line. +- Ignore chat key presses in demo playback and in -solo-net + +Links for the demos, wads and topics referred to above: +http://www.doomworld.com/idgames/index.php?id=12647 (niveles.wad) +http://www.doomworld.com/idgames/index.php?id=8808 (deca.wad) +http://www.doomworld.com/idgames/index.php?id=15125 (epic.wad) +http://chexmania.tripod.com/downloads.htm (chex.wad) +http://www.doomworld.com/idgames/?id=15420 (chex.deh) + +2.4.8.2 @ 2007.10.16 +[!] Updated to the latest prboom 2.4.8. (SVN build, revision 2423) +[+] New mouse code without SDL lags. Win32 mouse handling was removed. +[+] Boom colormaps are supported in OpenGL mode. +[+] New mus -> mid conversion code thanks to Ben Ryves This removes bugs and plays back a lot of music closer to Vanilla Doom - eg. tnt.wad MAP02. +[+] Smooth movement: interpolation of weapon bobbing. +[+] New command-line switch "-emulate prboom_ver" for emulation of old buggy behaviour of previous prboom versions during playback of doom\doom2\boom\mbf\lxdoom demos in cases where a prboom bug affects the demo compatibility and leads to desyncs in the original EXEs. Now you can specify only "-emulate 2.2.6" instead of set of -force_remove_slime_trails, -force_boom_brainawake, etc. For example, boom-incompatible demo by Anima Zero cc2lmps.zip\c215-uv.lmp recorded with prboom 2.2.4 in boom compatibility mode can be correctly viewed in newer versions with -emulate 2.2.4 or with -force_prboom_friction. +[+] PC Speaker emulation from chocolate-doom. +[+] New command-line switch "-force_lxdoom_demo_compatibility" for emulation of all bugs in demo compatibility mode in lxdoom. There are no more desyncs on "Doom-incompatible" demos from DHT6 exams. +[+] Ability to force -nomonsters and -respawn for playback of 1.2 demos. Demos recorded with Doom.exe 1.2 did not contain any information about whether these parameters had been used. In order to play them back, you should add them to the command-line for playback. There is no more desynh on mesh.lmp @ mesh.wad (prboom -iwad doom.wad -file mesh.wad -playdemo mesh.lmp -nomonsters) +[+] There is a new command-line switch "-shorttics". This makes it possible to practice routes and tricks (e.g. glides, where this makes a significant difference) with the same mouse behaviour as when recording, but without having to be recording every time. +[+] Wipe screen effect in OpenGL. +[+] Support for Hi-Res textures and patches in OpenGL. +[+] Import the clipper code from gzdoom. Thanks to GrafZahl. PrBoom uses a clipper with screen pixel precision. It isn't precise enough for GL. For GL we need a clipper with angular precision to avoid some kind of glitches. +[*] Events queue will not be cleared at the start. (like vanilla does) +[*] "No Quit Sound" is renamed to "Fast Exit". EndDoom screen will be skipped if this option is turned on. +[*] Effect of invulnerability uses a colormap in OpenGL mode instead of hard-coding. See nuts.wad as example. +[*] "Sky is unaffected by invulnerability" compatibility setting (comp_skymap) works in OpenGL mode now. +[*] "Paper Items" setting is not applied to hanging things. +[*] The player's weapon is displayed much darker than in 'vanilla' and other ports. In this version the mistake is corrected only for software rendering. +[*] Improved support for Doom v1.2: Projectiles, such as monster and player plasma and rockets, were able to activate down-wait-up type lifts in versions of Doom up to v1.2. +[*] Monsters on automap are drawn on top of other things to avoid situations when red triangles (live and countable monsters) are hidden by the green triangles for non-countable things. +[*] Show a warning if savegame is from the previous version of prboom-plus. +[*] "-recordfromto" works for all levels of compatibility. +[*] Improved precision. There are no more HOM's on maps with big open areas: nuts.wad at some positions, thespir2.wad at the start, "Generator of Evil.wad" in last room, MAP05 @ epic.wad everywhere, etc. +[*] Huge speedup in OpenGL on levels with sectors which have many lines. I've got 87 fps (timedemo) on MAP05 @ epic.wad in this version instead of 64 in 2.4.8.1. +[*] Compatibility with common mapping errors "linedefs w/o tags apply locally" works with Walk and Gunfire triggers (not only with switches) as in ZDoom. +[-] Non traditional menu was removed to avoid problems with, e.g., Alien Vendetta. +[-] Launcher: wrong strings in history combobox. +[-] Fixed Boom incompatibilities. There are no more desyncs on Donce's demos on horror.wad. +[-] Fixed Boom incompatibilities. Original friction and bobbing code was restored. There are no more desyncs on cybart.lmp @ Artica.wad and dismlmp.lmp @ dismem.wad. "-force_prboom_friction" for emulation of old buggy behaviour. +[-] %DOOMWADDIR% had no effect for PWADs and DEHs. +[-] A strip of rendered graphics could sometimes be seen under the status bar, along the bottom during intermission screens and in the rollcall screen. +[-] With OpenGL rendering, many of the translucent windows in MAP29 @ cchest2.wad are visible from one side only. +[-] Boom bug: Fixed incompatibility with original EXE's (complevels 0..6) on levels with "Monster Spawner". "-force_boom_brainawake" for emulation of old buggy behaviour. There is no more desync on 300340HR.lmp @ RlmChaos.wad. +[-] When radsuit ends, palette changes w/o warning. +[-] When playing back with -recordfromto, and before resuming recording, pausing causes playback to desync. +[-] OpenGL: Fix an issue with lines (boxes) which appear around the elements that change on the intermission screens in Doom1. +[-] When a file with the .BEX extension is used in an autoloading pattern, the program fails to load it. +[-] Boom bug: Correction for DEHs which swap the values of two strings. For example: Text 6 6 - RUNNINSTALKS; Text 6 6 - STALKSRUNNIN. There are no more bugs on MAP05 @ Allhell.wad. +[-] Kills percentage, if extremely high, was reported wrongly and could in theory lead to desyncs. +[-] PrBoom bug: Par times were not displayed. The program was viewing prboom(-plus).wad as a pwad, and therefore the piece of code that suppressed par times was always being triggered. +[-] Sometimes the pistol sounds that play while the tallies are being totaled on the end screen will play for too long after the completion time has finished tallying. +[-] There is no more crash on e1cmnet3.lmp between e1m2 and e1m3. +[-] Smart totals: Monsters spawned by an Monster Spawner should not be countable for total killed. +[-] Avoid crashes at end of demos if DEMOMARKER (0x80) does not exist at the end. +[-] ScreenShot feature did not work with "-videodriver directx" on fullscreen software. +[-] There are no more visual glitches with sky on MAP14 @ Icarus and MAP20 @ Hell Revealed. GZDoom still has this bug. +[-] Fix of no warning (flashes between shadowed and solid) in OpenGL when invisibility is about to go. +[-] Fixed wrong finding of next highest floor (linedef action 18, 20, 131) for demo compatibility. There are no more desyncs on napalm.wad. +[-] The priority system is broken in the ouch face code in a way that makes the face not work with monster damage. +[-] Moved mouse sensitivity menu upwards a little to avoid overlap with status bar with accompanying visual glitches on software renderer. +[-] The bug in the algorithm for splitting of a sector into closed contours was fixed. There is no more HOM at the starting area on MAP16 @ Eternal.wad in OpenGL. I hope nothing was broken. +[-] Correction of an overflow in calculation of a sound origin on large levels. New code is applied only for non-compatible modes and with comp_sound equal to zero. There is no more bug with an absent sound after pressing the distant western switch on Hell Spirit (not released) by Alexander "Eternal" S. +[-] Premature program exit during MAP26 in 4-player coop demo 29uv4p_*.lmp @ doom2.wad. This bug was introduced in r1458 by cph for 2.4.3 release. +[-] Wrong calculation of weapon width in OpenGL in some cases, especially during movement. Shotgun in DSV3_War looks correct now. +[-] OpenGL: Blink during demoskip on fullscreen. +[-] OpenGL: Bug with duplicated lines, e.g. MM.WAD Map22 lines 1298 and 2397. There is no more HOM on Memento Mori MAP22 sector 299. +[-] Fixed slowdown during screen melt at 1024x768 on some systems. +[-] Transferred sky texture on scrolled wall were not scrolled with mouselook or changed FOV. +[-] Sky property-transfer linedef types (271, 272) should not be applied for BOOM complevels. + +Links for the demos, wads and topics referred to above: +http://www.doomworld.com/idgames/index.php?id=13635 (HORROR.WAD) +http://competn.doom2.net/pub/sda/0-a/art_950.zip +http://www.doomworld.com/idgames/index.php?id=9832 (Artica) +http://competn.doom2.net/pub/sda/d-h/dis_0942.zip +http://www.doomworld.com/idgames/index.php?id=9787 (Dismemberment) +http://www.doomworld.com/idgames/index.php?id=11402 (NUTS) +http://competn.doom2.net/pub/sda/b-c/cc2lmps.zip +http://www.doomworld.com/idgames/index.php?id=6480 (allhell.zip) +http://competn.doom2.net/pub/compet-n/doom/coop/movies/e1cmnet3.zip +http://www.doomworld.com/idgames/index.php?id=13976 (MESH.WAD) +http://www.doomworld.com/idgames/index.php?id=15025 (Napalm) +http://www.idoom.cz/dema/lan/doom-open-2003/Exhibice.zip +http://www.doomworld.com/idgames/index.php?id=11009 (DSV3-War) +http://www.doomworld.com/idgames/index.php?id=14476 (The Spire 2) +http://www.doomworld.com/idgames/index.php?id=14975 (Generator of Evil) +http://www.doomworld.com/idgames/index.php?id=15125 (EPIC) +http://www.doomworld.com/idgames/index.php?id=11790 (Alien Vendetta) +http://www.doomworld.com/idgames/index.php?id=13024 (Community Chest 2) +http://www.doomworld.com/idgames/index.php?id=10513 (Realm of Chaos) +http://www.doomworld.com/idgames/index.php?id=5191 (ICARUS: ALIEN VANGUARD) +http://www.doomworld.com/idgames/index.php?id=7947 (Hell Revealed) + +2.4.8.1 @ 2006.12.26 +[!] Updated to the latest prboom 2.4.8 (SVN). +[+] Compatibility with common mapping errors: "Walk underneath solid hanging bodies". Thanks to RjY. +[+] Launcher: Autoselecting of wads according to the lmp file-name (using regular expressions) +[+] Two new compatibility options are added: + comp_ouchface - "Use Doom's buggy 'Ouch' face code" + comp_oofsound - "Dead players make 'oof' sound when landing" +[+] New "-auto" command-line switch for autoloading of wads according to the lmp file-name. +[+] There are three modes of sprite clipping in opengl: + Constant - gl_sprite_offset used; + Always - if the sprite is below the floor, and it's not a hanger/floater/missile; + Smart - if the sprite is below the floor, and it's not a hanger/floater/missile, and it's not a fully dead corpse; +[+] Demo progress bar works during skipping. +[+] Improved handling of unrecognized demo formats. Legacy and Boom v2.00 demos do not crash PrBoom-Plus now. +[+] "Use" key ("space" by default) during skipping will show the current frame. +[+] New "all-in-one" version that includes the resource wad and SDL files inside the exe. +[*] Compatibility with common mapping errors "Pad a too short reject with zeros" was removed. Added new command line switch "-reject_pad_with_ff". PrBoom 2.2.5 and 2.2.6 padded a short REJECT with 0xff (ones) instead of zeros. This command-line switch may be needed for demos recorded with these versions of PrBoom on maps with a too short REJECT. For example: lwfirst.wad\equ7-108.lmp. +[*] Improvements in text labels: + "Pain elemental limited to 20 lost souls" -> "Pain elementals limited to 21 lost souls" + "All boss types can trigger tag 666 at ExM8" -> "Emulate pre-Ultimate BossDeath behaviour" + "Ultimate Doom v1.9" -> "Ultimate Doom/Doom95" + "Final Doom/Doom95" -> "Final Doom" +[*] Removing startup delay during recording. You can record demos from the first tic now. +[*] Correction in "smart items clipping": a not fully dead corpse should be moved up, exploding barrels for example; a missile should not be moved. +[*] Improved support for Doom v1.2: + Max Health Bonus = 199; + Max Armor = unlimited; + Max Soulsphere = 199; + Megasphere Health = 199 (there was no megasphere back then, but this seems consistent); + Lost souls "Affect Kill %"; + Monsters could commit suicide if they damaged themselves by exploding a barrel; +[*] "Total Level Times" are saved with any level of compatibility. +[-] Launcher: WADs not containing levels were not displayed in the files list. +[-] GLBoom bug: Things disappeared behind translucent things sometimes. gl_sortsprites has no effect now. +[-] GLBoom bug: Wrong (with holes) drawing of the background for the loadgame and savegame entries. +[-] GLBoom bug: Wrong alignment of texture with 'lower unpegged' flag on two-sided walls in upper part. Linedef 527 on STRAIN.WAD\MAP09 for example. +[-] Eliminating the six-tic jump when changing speed. +[-] Boom bug: Generalized effects could work instead of failing in a 'vanilla' compatibility mode. "-force_truncated_sector_specials" for emulation of old buggy behaviour. There are no more desyncs on vv2.wad\map06, etc. +[-] Boom bug: Fix of hanging decoration disappearing in Batman Doom MAP02. Lee Killough's math should be rechecked there. +[-] PrBoom bug: Pausing during intermission in playback could cause desyncs. +[-] PrBoom bug: Bug in handling of common strings in deh files introduced by cph in PrBoom 2.4.2. +[-] PrBoom bug: comp_doorstuck should be forced (but was not) for boom201 compatibility. There is no more desync on DEMONIPA.WAD\demp-208.lmp +[-] PrBoom bug: The behaviour with comp_666 was that the death of all barons on E2M8 or E3M8 ends the level, heh. This is incorrect; it should have no effect. The bug was introduced in 2.4.x by cph. After some tests and disassembling I conclude that the implementation of A_BossDeath() in all compatible ports (PrBoom, Eternity, Chocolate-Doom) is totally wrong for doom2 compatibility. This version should fix the buggy behaviour. There is no more desync (premature exit) on doom.wad\episode3.lmp +[-] Predefined translucency setting (comp_translucency) could override deh settings. +[-] Sometimes the figures for kills, items, etc., stopped getting updated. +[-] PrBoom bug: Fixed Boom v2.01 incompatibilities. There are no more desyncs on demonipa.wad\demp-800.lmp, tomb.wad\tomb.lmp, venom.wad\ven-1116.lmp. +[-] PrBoom bug: Fixed an MBF incompatibility. There is no more desync on vrack2.wad\v2-2822.lmp. "-force_no_dropoff" command-line option is for mbf_compatibility demos recorded with prboom 2.2.2 - 2.4.7 in case of desyncs. +[-] PrBoom bug: Weird texture problem. (spacelab.wad - GL only) + +Links for the demos, wads and topics referred to above: +http://www.doomworld.com/idgames/index.php?id=14219 +http://competn.doom2.net/pub/sda/d-h/equxlmps.zip +http://www.doomworld.com/idgames/index.php?id=8467 +http://www.doomworld.com/idgames/index.php?id=14582 +http://www.doomworld.com/sda/dwdemo/vv206324.zip +http://www.doomworld.com/idgames/index.php?id=12391 +http://www.doomworld.com/idgames/index.php?id=3336 +http://competn.doom2.net/pub/sda/d-h/demp-208.zip +http://www.doomworld.com/idgames/index.php?id=6909 +http://competn.doom2.net/pub/sda/d-h/demp-800.zip +http://www.doomworld.com/idgames/index.php?id=3336 +http://competn.doom2.net/pub/sda/t-z/tomb_850.zip +http://www.doomworld.com/idgames/index.php?id=8148 +http://competn.doom2.net/pub/sda/t-z/ven_1116.zip +http://www.doomworld.com/idgames/index.php?id=9215 +http://competn.doom2.net/pub/sda/t-z/v2-2822.zip +http://www.doomworld.com/idgames/index.php?id=11138 +http://www.doomworld.com/idgames/index.php?id=6826 + +2.4.6.1 @ 2006.09.16 +[!] Updated to the latest prboom 2.4.6. +[+] Mac specific: OpenGL version is available for Mac too. +[+] Holding down SHIFT during startup for bringing the launcher up even if you have disabled it in settings. +[+] Option to disable "doubleclick as use" behaviour. +[+] Option "Max View Pitch" for restriction of the maximal looking up/down angle. (0-90) +[+] "-spechit XXX" command line switch. +[+] Compatibility with common mapping errors: "Pad a too short reject with zeros". +[+] Autodetect of unconverted tas demos. +[+] Options for showing progress bar during demo playback and demotime/totaldemotime style as alternative. +[+] New Screen Multiple system (like in chocolade-doom) and Interlaced Scanning for this. (emulators and TV style) +[+] New "-videodriver name" command line switch (sdl_videodriver variable in config) for setting up the videodriver name that SDL will use. The "windib" video driver is the default now, to prevent problems with certain laptops, 64-bit Windows, and Windows Vista. The "directx" video driver is forced by PrBoom-Plus only for soft rendering on Win9x systems to prevent some problems on out-of-date systems. Also, you can use the "default" value to force SDL behavior by default. +[+] Possibility to enable/disable the Launcher from its window. +[+] Launcher: iwads are now shown in the files list so that they can be used as pwads +[*] Default key to take over the controls with resume recording feature was changed to 'q'. Default key for switching between chasecam/walkcam/normal views was changed to 'KeyPad0'. +[*] "-playdemo demo -warp X -skipsec Y" will skip Y seconds on X level. +[*] Reconfiguration in settings. Most of the PrBoom-Plus settings are moved to General. +[*] Config entry for "uncapped framerate" was renamed from "movement_smooth" to "uncapped_framerate" as in upcoming PrBoom. +[*] Uncapped framerate: speed update up for HUD on slow systems. +[*] Forcing "directx" video driver for software rendering in Win9x. +[*] Alt mouse handling: Do not grab mouse if the application has no keyboard focus. +[*] Alt mouse handling: Disabled for win9x. +[-] Mac specific: Mouse did not work with switched on "alt mouse handling". Disable this feature for non-windows platforms. +[-] Fix a crash after pressing the walkcam key at the intermission screen, the game final animation, or a demo. +[-] IDRATE cheat shows correct FPS with uncapped framerate. +[-] Fix for incorrect chasecam/walkcam sight after loading of the next level. +[-] Fixed crash with newest sdl_mixer.dll after skipping level during playdemo. +[-] GLBoom: Correction of displaying (HOM) of the textures with holes (MIDBARSX, MIDGRATES, etc) on the one-side walls at OpenGL. fenris.wad as example. +[-] PrBoom bug: Fixed crash with zero-size BLOCKMAP lumps. +[-] PrBoom bug: Fix compatibility issue with building stairs. There is no more desync on icarus.wad\ic29uv.lmp +[-] PrBoom bug: Fix compatibility issue was introduced by killough long time ago. There is no more synch :) on dv.wad\dv04-423.lmp. Added switch to force old behavior with demo_compatibility: -force_remove_slime_trails. It is relevant for viewing doom-compatible demos recorded with old versions of prboom (<2.4.6) or prboom-plus (< 2.4.6.1) in cases of desyncs. +[-] PrBoom bug: Fix another compatibility issue. It corrects half of problem of desync on Wotdoom3\w303-115.lmp. +[-] Boom dehacked support: Correction of wrong processing of "Respawn frame" entry. Highly relevant to wads such as Wotdoom3 and any others that change this setting. There is no more desync on Wotdoom3\w303-115.lmp. +[-] PrBoom bug: Fix compatibility issue with buggy MBF behavior (3-key door works with only 2 keys). There is no more desync on 10sector.wad\ts27-137.lmp. +[-] PrBoom bug: Wrong scaling of status bar at some resolutions. Strongly noticeably on ARMS section at 320x240. +[-] Alt mouse handling: Ungrabbing of mouse was not being done before a quit in some cases. +[-] PrBoom bug: Fixed some tasdoom incompatibility. There is no more desync on ddt-tas.zip\e4tux231.lmp. Nowhere no hide. +[-] PrBoom bug: Conflicting command-line parameters could cause the engine to be confused in some cases. Added checks to prevent this. Example: glboom-plus.exe -record mydemo -playdemo demoname +[-] Launcher: Fake iwads could be in the game list. Added a check for "IWAD" signature to prevent this. + +Links for the demos, wads and topics referred to above: +http://www.doomworld.com/tas/ts27-137.zip +http://prboom.sourceforge.net/mbf-bugs.html +http://www.doomworld.com/sda/dwdemo/dv04-423.zip +http://competn.doom2.net/pub/sda/i-o/icuvlmps.zip +http://www.doomworld.com/idgames/index.php?id=5191 +http://www.doomworld.com/sda/dwdemo/w303-115.zip +http://www.doomworld.com/idgames/index.php?id=13815 +http://www.doomworld.com/vb/showthread.php?s=&postid=627257#post627257 + +2.4.3.1 @ 2006.07.25 +[!] Updated to the latest prboom 2.4.3. +[!] PRBoom-Plus is now available on UNIX, Linux, and other POSIX systems and also on Mac. +[+] Several predefined cfgs and batch launcher for them are included. +[+] Simple aspect ratio adjustment made possible in OpenGL. It may be useful for people with unusually shaped monitors. Some usage examples: + glboom-plus.exe -aspect 320x240 + glboom-plus.exe -aspect 2x1 +[*] Changes in processing of spechit overflow for correct playback of some Xit's demos on scythe2.wad\map02. +[*] Roll back to old SDL library. Some people had some problems with new. +[*] The maximal supported resolution is increased from 1600x1200 up to 2048x1536. +[*] GLBoom will try to set the closest supported resolution if the requested mode can't be set correctly. For example glboom.exe -geom 1025x768 -nowindow will set 1024x768. It affects only fullscreen modes. +[*] The maximum walking side speed for strafe50 is 0x19. +[*] The prboom's savegamename mask "prbmsav" was renamed to "prboom-plus-savegame". Also: + prboom_server.exe -> prboom-plus_server.exe + prboom.exe -> prboom-plus.exe + glboom.exe -> glboom-plus.exe + prboom.cfg -> prboom-plus.cfg + glboom.cfg -> glboom-plus.cfg + prboom.wad -> prboom-plus.wad. +[-] Fix from Eternity (adapted by RjY) for ancient doom software render overflow that happens when the player goes over a linedef between two sectors of extreme height. There are no more crashes on thespir2.wad. +[-] At use of the keyboard or joystick for strafing, prboom+ always generated strafe50 tics on turns independently of value of a corresponding option. +[-] The launcher did not select last entry from history after start. +[-] Problems with sky on thespir2.wad in opengl. + +2.2.6.29 @ 2006.06.08 +[+] Detection and emulation of spechit overflow for dosdoom and tasdoom compatibility. There are no more desyncs in the following dosdoom demos: flsofdth.wad\fod3uv.lmp, hr.wad\hf181430.lmp, hr.wad\hr181329.lmp, icarus.wad\ic09uv.lmp +[+] Support for comp_666 and comp_maskedanim +[!] Update to newest SDL libraries. SDL.dll ver.1.2.10.0, SDL_mixer.dll ver.1.2.7.0, SDL_net.dll ver.1.2.6.0 +[!] Some variables specific to OpenGL have been removed from the list of variables meant for prboom.cfg +[!] The launcher will not be displayed if IWAD or PWADs are specified in a command-line. +[!] The new PrBoom-plus entries in the configs are preceded by a title. +[-] PrBoom bug: The sky in the third episode of Requiem was not drawn. It did not work correctly because the SKY3 has a zero index in the TEXTURE1 table. Textures with a zero (FALSE) index are not displayed in vanilla - AASHITTY in doom2.wad for example. But the sky textures are processed by different code, which does not have this bug. This bug is also present in the current GZDoom, but it only draws the wrong sky instead of drawing no sky at all. +[-] PrBoom bug: The "Show coordinates of automap pointer" setting has no effect. +[-] FOV had side effect on software rendering. +[-] Alt mouse handling: Eternal rotation of the player at non-standard modes on software mode. Example: + prboom.exe -geom 1025x768 -nowindow + +Links for the demos referred to above: +http://www.doomworld.com/sda/flsofdth.htm +http://www.doomworld.com/tas/hf181430.zip +http://www.doomworld.com/tas/hr181329.zip +http://competn.doom2.net/pub/sda/i-o/icuvlmps.zip + +2.2.6.28 @ 2006.05.09 +[+] Option to turn off "Paper Sprites". It works only with mouselook, and is only for non-solid sprites. +[!] The command-line parameter "-screenscapture" is renamed to "-avidemo". Also syntax has changed. +[!] Reconfigurations in settings. +[-] Screenshots now use correct palette in software mode. +[-] Screens capturing: do not dump black screens while in skip mode. +[-] Respawned monsters on nightmare do not have countable flag. +[-] Mouse-induced Strafe50 couldn't be achieved. + +2.2.6.27 @ 2006.04.05 +[+] PrBoom compatibility: two new options for intercepts overflow detection. It detects and tries to emulate overflows on some odd demos like blackbug.lmp, manorbug.lmp, hr27odd.lmp, av08-odd.lmp, etc. +[+] PrBoom compatibility: two new options for playeringame overflow detection. It detects and emulates overflows on vex6d.wad\bug_wald.lmp, vex6d.wad\bug_toke.lmp, etc. It works without crashing in Eternity and chocolate-doom only by good fortune. +[+] Cheating: new command line switches: -trace_thingshealth, -trace_thingspickup, -trace_linescross. Usage: + -trace_thingshealth ThingID [ThingID] [ThingID] + -trace_thingspickup ThingID [ThingID] [ThingID] + -trace_linescross LineID [LineID] [LineID] +[+] Autorun: the "RUN" key inverts the autorun state +[!] PrBoom compatibility: minor changes in processing overflow of spechit array. It became possible after decompiling of DOS EXE. +[!] Names of variables responsible for overflows and their default value have been changed. It's switched on for emulation and switched off for warnings now. +[-] Smooth turns in demos: movements of first player in a net-demo were not smooth if the second player is dead. +[-] GLBoom: from certain angles and distances, things behind a translucent texture become invisible (test). +[-] Boom bug: desyncs after using the key for switching to SSG directly with demo compatibility. +[-] Launcher: the selected IWAD was loaded after preloaded wads. +[-] Detail texture: detail texture moves slower than scrolling floors. +[-] Detail texture: fixes for old videoadapters which do not support gl_arb_multitexture extension (still buggy). +[-] Impossible to change game speed when viewing a net-demo (2.2.6.25). + +2.2.6.26 @ 2006.01.31 +[+] Launcher: Activated when no wad is specified in the command line. +[+] Smooth movement: scrolling textures are interpolated. +[+] Spechits overflow lines are listed in prboom.exe. +[+] New command-line option (-net1) for single-player coop mode that works for play, recording and playback. +[+] New command-line options for setting a window (-window) or fullscreen (-nowindow) mode temporarily which is not saved in cfg. +[+] Compatibility option to disable translucency applied to certain Things. +[*] Spechit overflow: the size of emulated overflow is limited by 14 lines. +[*] "Smooth movement" renamed to "Uncapped framerate". +[-] PrBoom bug: bug in MBF compatibility mode (version number written wrongly in lmp header when recording). +[-] PrBoom bug: translucency via dehacked/bex doesn't work. +[-] PrBoom bug: DEH files preloaded in wrong order. +[-] PrBoom bug: "Tagged doors don't trigger special lighting" handled wrongly. +[-] prboom-server bug: -1 => 255 instead of maxcompat (14) +[-] Alt mouse handling + OpenGL + Capped framerate + Fullscreen + server = mouse turning disabled (2.2.6.24) +[-] Bug with DEH-files in BEX-Format (2.2.6.23) +[-] The engine grab the mouse when it's waiting for the net game to start (2.2.6.24) + +2.2.6.25 +[+] PrBoom compatibility: two new options for detection of overflow of "REJECT". It's emulated successfully if the size of overflow no more than 16 bytes. No more desync on teeth-32.wad\teeth-32.lmp. +[+] Compatibility with common mapping errors: "Linedefs w/o tags apply locally". If a linedef type that normally requires a tag (e.g. a remote door) has no tag, then if possible apply the linedef's action to the local sector. Not available for recording, playback or with demo_compatibility. +[+] Compatibility with common mapping errors: "Use passes thru all special lines". Boom's passthru flag is applied to all special lines. This makes it possible, for instance, to press switches that have been placed behind special lines such as scrolling textures. Not available for recording, playback or with demo_compatibility. +[+] Compatibility with various early Doom versions and ports (for playback, overriding autodetect): + Doom & Doom2, v1.666 : -complevel 1 + Doom2 doom2.exe v1.9 : -complevel 2 ;No more desync on uac_dead.wad\uac_dead.lmp. + Ultimate Doom v1.9 : -complevel 3 + Final Doom doom2.exe : -complevel 4 + DosDoom : -complevel 5 ;No more desync on demos from old TAS site + TasDoom : -complevel 6 ;And some of Andy Olivera's demos. +[!] Network game works again. +[!] Allowing to mouselook during recording. +[!] Interdiction of change of game speed during network game. +[!] PrBoom+ will not show the advanced statistical information (Smart Totals) in deathmatch. +[!] If file already exists, don't try to continue existing demo with demo_compatibility. +[-] Crash with zero-length sounds. +[-] Bug with DEH-files in BEX-Format (2.2.6.23) +[-] Smooth movement: elevators were not interpolated. Sector #137 @ dmdjm02.wad for example. +[-] Boom dehacked support: wrong processing of Max Health. A new compatibility option has been added so that it applies only to health potions. Highly relevant to wads such as Hacx, Wotdoom3, Anadream, and any others that change this setting. +[-] Issue with dehacked floating monsters in glboom.exe if "Smart Items Clipping" is turned on (2.2.6.20) + +2.2.6.24 +[+] Option for removing the "wipe" effect. +[+] Option for smoother mouse control. +[+] Mouse acceleration code. +[+] Ability to use savegames while using a complevel. +[!] PrBoom compatibility: changes in processing overflow of spechit array. +[!] The "-recordfrom" switch is renamed into the "-recordfromto" +[-] PRBoom.exe: the screen ceases to be updated after start of new game while you are already in game. +[-] Problems with status bar in prboom.exe (2.2.6.21) +[-] -skipsec broken (2.2.6.23) +[-] Few improvements in the sky rendering. No more HOM in the starting area of Memento Mori map29 and an minor issue in map30. +[-] Translucency won't change until you restart the engine. + +2.2.6.23 +[+] Dehacked support: Monsters infight. +[-] PrBoom: It crashes when you set game speed over 1000. +[-] "-warp" switch didn't work. (the bug was introduced in 2.2.6.22) + +2.2.6.22 +[+] PrBoom compatibility: two new options for spechit overflow detection. It detects desyncs like compet-n\hr.wad\hr18*.lmp, all strain.wad\map07 demos etc. +[+] Added "-levelstat" switch for levelstat.txt output like this: + E4M1 - 0:15.91 (0:15) K: 8/63 I: 5/21 S: 0/2 + E4M2 - 0:10.97 (0:25) K: 8/76 I: 6/22 S: 1/3 + E4M3 - 0:08.86 (0:33) K: 1/140 I: 1/5 S: 0/22 + E4M4 - 0:32.43 (1:05) K: 19/60 I: 11/23 S: 0/2 + E4M5 - 0:22.89 (1:27) K: 10/73 I: 0/29 S: 0/2 + E4M6 - 0:23.66 (1:50) K: 21/97 I: 0/4 S: 0/3 + E4M7 - 0:09.94 (1:59) K: 6/97 I: 1/18 S: 0/4 + E4M8 - 0:33.94 (2:32) K: 39/106 I: 1/6 S: 0/1 +[+] New key binding for warp directly to the next stats screen. by default. +[!] Option "step of speed change" has been restored. Two modes are available: stepwise and automatic. +[-] PrBoom dehacked support: wrong processing of Bits parameter if its value is equal to zero. No more desync on HACX demos. +[-] PrBoom compatibility: PRBoom doesn't spawn the boom-specific "specials" in demo_compatibility mode now (generalized scrollers, sector effects). No more desync on umbrella.wad\um3nm152.lmp. +[-] PrBoom compatibility: MF_JUSTHIT fix. No more desync on Sam Woodman's HMP Max demos (HX17-459.LMP etc). +[-] PrBoom: fix for turn-snapping bug on fullscreen in software mode. "..\Advanced HUD settings\Movements\Fix Turn-Snapping issue" +[-] PrBoom: deathmatch starts as unknown things with a complevel of 0, 1, 2 or 3. +[-] GlBoom: problems with transfering not flipped sky texture (line type 271) to tagged sectors. On war_3.wad\map14 for example. +[-] PrBoom-Plus: mistake of processing of the "quick rewind" key if the player dies. + +2.2.6.21 +[+] Added switch to force monster_avoid_hazards: -force_monster_avoid_hazards. It is meaningful for viewing doom-compatible demos recorded with "monster_avoid_hazards 1" in config (in cases of desyncs). +[+] Renders bsp tree in automap mode. +[!] Allows mouselook during playdemo in camera mode. +[!] FOV units have been adjusted to conform with commonly used scale. (64=>90) +[!] The step of gamespeed change varies automatically depending on current speed. Option "step of speed change" is removed. +[-] Wrong keys size in automap mode. (the bug was introduced in 2.2.6.19) + +2.2.6.20 +[+] GLBoom: multisampling (anti-aliasing) support. +[+] GLBoom: "Smart Items Clipping" setting. +[-] Detail texture: dividing by zero. + +2.2.6.19 +[+] Support up to 65536 sidedefs instead of 32768. + +2.2.6.18 +[+] "Mouse Look" sprite for "Mouse Sensitivity" in menu. +[+] Demo recording: "overwrite existing" options for overwrite of the existing demo if no savegame presents. +[*] Vert sens. +[*] Smooth movement: more smooth movements at low FPS. +[-] Bugs with no mipmap. +[-] Smooth turns in demos: wrong direction of a player sight after loadgame request caused from the demo. + +2.2.6.17 +[-] Smooth movement: bugs in algorithm: finally it works perfectly. + +2.2.6.16 +[-] Bugs of the previous version. +[-] Smooth movement: bugs in algorithm appearing at low framerate. + +2.2.6.15 +[*] Full mouselook: a big increase in FPS if the viewpitch of the player is in the range from -45 to +45 degrees. +[*] Smooth movement: minor changes. + +2.2.6.14 +[*] Smooth turns in demos: changes in algorithm. Now smoothing occurs in a range [currtick-smoothfactor..currtick] instead of [currtick-smoothfactor..currtick+smoothfactor] +[-] Smooth turns in demos: wrong direction of a player sight after respawn. + +2.2.6.13 +[-] Smooth turns in demos: bug arising after forcibly changing of the player's viewangle by the engine at use of a saw or a fist. (Grazza) + +2.2.6.12 +[*] Smooth turns in demos: changes in algorithm. Now smoothing occurs in a range [currtick-smoothfactor..currtick+smoothfactor] instead of [currtick-smoothfactor..currtick] +[-] Smooth turns in demos: turns did not smooth out if the option of smooth movements has been switched off. +[-] Smooth turns in demos: problems when the demo includes very sharp turns. (Grazza) + +2.2.6.11 +[+] Demos: option for smooth turns during viewing of demos. It makes sense for TAS demos recorded with slowmotion. +[-] Smooth movement: some flickers after menu closing. +[-] PRBoom issues: prboom uses monster_avoid_hazards setting from the cfg file when you are using -complevel 0 or -complevel 1. If you have this option set to 1 in your cfg, then you are at risk of getting desyncs in demos that feature crushers. (Dashiva) + +2.2.6.10 +[+] GLBoom: detail texture support for walls and flats. Requires hi-end systems. + +2.2.6.9 +[-] Bugs in the new algorithm of middle textures drawing. + +2.2.6.8 +[*] Not drawing the player gun sprite in the freely rotatable view mode (Janizdreg). +[-] Run the game with the -nomusic parameter, start a new game (via the menu) and either kill yourself or start a new game again and the music will start playing. (Janizdreg) +[-] Smooth movement: interpolation did not work for moving platforms after savegame load. +[-] GLBoom bug: wrong display of a middle texture if it exceeds the boundaries of its floor and ceiling. + +2.2.6.7 +[-] Smooth movement: "I_SignalHandler: Exiting on signal: 11" bug during rendering of the first frames of heavy scenes (after opening doors). + +2.2.6.6 +[-] Smooth movement: doors activated by lines of the generalized types were not interpolated. +[-] Mouselook & fov: bugs in sky rendering. + +2.2.6.5 +[-] If a new game is started while a demo is playing the walkcam settings are carried over to the single player game. + +2.2.6.4 +[+] Full mouselook and FOV in GLBoom (without compatibility loss). +[*] Smooth movement: few changes in algorithm. + +2.2.6.3 +[-] Smooth movement: blinking in the second level of the Cchest2.wad. +[-] Smooth movement: lifts activated by lines of the generalized types were not interpolated. diff --git a/doc/prboom-plus-usage.txt b/doc/prboom-plus-usage.txt new file mode 100644 index 0000000..d198c22 --- /dev/null +++ b/doc/prboom-plus-usage.txt @@ -0,0 +1,400 @@ +There are some additional compatibility settings available on: +Main Menu\Options\Setup\Doom Compatibility (on last page) + + All boss types can trigger Tag 666 at ExM8 ; Emulates the BossDeath behaviour of pre-Ultimate + versions + Lost Souls don't bounce off flat surfaces ; Emulates the lost soul behaviour of pre-Ultimate + versions (this includes Doom2.exe) + 2S Middle textures do not animate ; Emulates the behaviour of Doom.exe version 1.2 with + respect to this. + Retain quirks in Doom's sound code ; This is for the comp_sound option, which was previously + missing from this menu; note that comp_oofsound has been + merged with comp_sound + sound when landing from a significant height. + Use Doom's buggy 'Ouch' face code ; Restores the "vanilla" Doom for the displaying of the + "ouch" face - it is displayed when the player takes + damage but overall gains >20% health. + Max Health in DEH applies only to potions ; Corrects Boom's handling of the DeHackEd Misc option + "Max Health ="; it should apply only to health potions + No predefined translucency for some things ; Disables the Boom feature of making 17 of the Things + translucent by default + +Several additional key-bindings and settings are available on: +Main Menu\Options\Setup\Key Bindings (on last page) + + SPEED + Speed Up ; Increase speed of the game + Speed Down ; Decrease speed of the game + Reset to Default ; Sets speed of the game to the default + Step of Change (0-auto) 0 ; The step of gamespeed change and smart mode if it is equal to zero + DEMOS ; Keys for additional functions during demo playback + Next Level ; Fast forward to the next level (disabled for internal demos) + End Level ; Fast forward to the next stats screen (disabled for internal demos) + ; Pressing Use ("Space" by default) during skipping shows the current frame. + Camera Mode ; Walkcam/Chasecam + Join ; Take over the controls with resume recording feature (*1) + +Additional HUD settings are available here: +Main Menu\Options\Setup\Status Bar / HUD (on last page) + + ADVANCED HUD SETTINGS ; HUD settings + Secret Areas ; Display on-screen message and play sound when a secret is registered + Smart Totals ; Advanced information about kills, items and secrets + Show GameSpeed ; Show speed of the game (100 by default) + Show LevelTime ; Total time and level time + Show DemoTime ; Displays the total time of the demo from first tic to last tic, + including stats screens and intermissions + Show Progress Bar During Demo Playback ; Displays a progress bar at the bottom of the HUD + +Additional PrBoom-Plus settings are available here: +Main Menu\Options\General + + OPENGL + Anisotropic Filter ; The setting can be changed from this menu option in-game. + + SOUND & MUSIC + PC Speaker Emulation ; Emulates the PC Speaker sound effects from vanilla Doom + Preferred Midi Player ; Choose how you want music playback to happen. + ; SDL: Use SDL_Mixer (same as 2.5.0.8 and earlier) + ; FLUIDSYNTH: Use fluidsynth to synthesize midi. You will need a soundfont file (sf2), and + ; you can specify its name with the cfg file option snd_soundfont. + ; Many soundfonts suitable for doom music can be found at http://www.richnagel.net/ + ; OPL2: Use an OPL2 chip emulator. (Sounds like AdLib or classic SoundBlaster) + ; PORTMIDI: Use the portmidi library for system output to the default midi device. + ; If your sound card has hardware midi support or you want to use a software + ; synthesizer that installs as a system device, like BassMidiDRV or Microsoft GS + ; Wavetable Synth, use this. + + VIDEO + Uncapped Framerate ; Removes 35 FPS restriction without loss of compatibility + + DEMOS ; Demos settings: file handling and viewing + Overwrite Existing ; When recording, overwrite an existing demo that has the same file-name + Smooth Demo Playback ; Make turns look smoother during demo playback + Smooth Demo Playback Factor ; (1-min..16-max), default value is 6 + + MOVEMENTS ; Movement settings + Permanent Strafe50 ; Turns on permanent strafe 50 (equivalent to using a strafe50 mousedriver) + Strafe50 on Turns ; Allow strafe50 while turning (impossible with original engine) + + MISC + Fast Exit ; The program exits as soon as the user has asked it to do so, rather than + waiting until the quit sound ends or showing the Endoom/Endboom screen. + Use In-Game Launcher ; A launcher will be activated whenever Prboom-plus is started up + Without a wad specified in the command line. + + MOUSE ; Mouse settings + Dbl-Click as Use ; Option to disable "doubleclick as use" behaviour. + + MOUSE / OPENGL ; Mouse settings available only with OpenGL rendering + Always Mouselook ; Mouselook toggle + Invert Mouse ; Invert mouse for mouselook + Max View Pitch ; Option for restriction of the maximal looking up/down angle. (0-90) + + COMMON RENDER ; Settings that apply with either renderer + Render Quality ; Offers a choice between Speed or Quality - the latter reduces the chance of + rendering imprecisions, particularly on some ATI cards + In software mode, 'Quality' can kill some "slime trails" and visplanes with + the same texture match up far better + Change Palette on Pain ; Red eyes on pain + Change Palette on Bonus ; Blinking on item pick-up + Change Palette on Powers ; Various visual effects on taking power-ups + Wipe Screen Effect ; Option to turn off (or on) the "wipe" screen effect + + OPENGL RENDER ; Settings that apply only with the OpenGL renderer + Multisampling (0-none) ; Anti-aliasing setting + Detailed Walls ; Use detail texture for walls + Detailed Flats ; Use detail texture for floors and ceilings + + Field of View ; Adjusts the field of view; default value is 90 + Paper Items ; Option to turn off "Paper Sprites". It works only with mouselook, and + is only for non-solid sprites. + Adjust Sprite Clipping ; Options for correcting errors in OpenGL rendering in the positioning of + items relative to floors and ceilings: + Constant - gl_sprite_offset used; + Always - if the sprite is below the floor, and it's not a hanger/floater/missile; + Smart - if the sprite is below the floor, and it's not a hanger/floater/missile, + and it's not a fully dead corpse; + Item out of Floor Offset ; Value for 'Constant' option in "Adjust Sprite Clipping". + Allow Colormaps ; Option to use a colormap (in iwad or pwad) for invulnerability effect, and to + use Boom's colormap feature - this comes at some cost in performance; if not + selected, Boom colormaps are not applied at all, and the invulnerability effect + is defined by hard-coding (NB: no monochrome effect), rather than by the wad + Allow Hi-Res Textures ; Use hi-resolution textures and flats, if present + Allow Hi-Res Patches ; Use hi-resolution patches (e.g. menu items, title screen), if present + Override Pwad's Graphics with Hi-Res ; If there is a hi-res texture (etc.) with the same name as a texture in + a pwad that has been loaded, use the hi-res one. Note that this can lead to very odd + results. This option is useful if loading plutonia.wad or tnt.wad *as a pwad*. + Sector Light Mode ; Choice between glboom, gzdoom and mixed; the 'mixed' lighting mode for opengl uses + gamma ramps like in gzdoom mode, but applies new gamma to textures. + + SOFT RENDER ; Settings that apply only with the software renderer + Screen Multiple Factor (1-none) ; Screen Multiple system for doubling up the screen + (size of pixel) by 2x, 3x or 4x. + Integer Screen Scaling ; Force integer scales for resolution-independent rendering. + + EMULATION ; Emulation of Doom2.exe's behaviour in case of memory overflows + Note that by default the warnings are turned off, and the + emulation turned on. + Warn on Spechits Overflow ; Provide an on-screen message if an overflow has occurred due to + more than 8 special lines being simultaneously crossed by a + player or a monster. The warning message includes a listing of + the lines responsible. + Try to emulate it ; Attempt to emulate Doom2.exe's behaviour in the case of a + Spechits Overflow. When using -complevel 5 or 6, Prboom-plus + instead attempts to emulate Dosdoom 0.47's behaviour. + Warn on REJECT Overflow ; Provide an on-screen message if a map's REJECT is too short. + The warning message includes an indication of the size of the + overflow and an indication of whether it can be reliably + emulated. + Try to emulate it ; Attempt to emulate Doom2.exe's behaviour in the case of a + REJECT Overflow. + Warn on Intercepts Overflow ; Provide an on-screen message if an overflow has occurred due to + MAXINTERCEPTS being exceeded, as in odd demos such as + blackbug.lmp, manorbug.lmp, hr27odd.lmp, av08-odd.lmp, etc. + Try to emulate it ; Attempt to emulate Doom2.exe's behaviour in the case of an + Intercepts Overflow. Note that this will tend to lead to + noclipping or allghosts behaviour. + Warn on playeringame Overflow ; Provide an on-screen message if an overflow occurs due to a rare + multiplayer issue seen in demos such as vex6d.wad\bug_wald.lmp, + vex6d.wad\bug_toke.lmp, etc. + Try to emulate it ; Attempt to emulate Doom2.exe's behaviour in the case of a + playeringame Overflow. Note that this will result in the + creation of a zombie player. + + COMPATIBILITY WITH COMMON MAPPING ERRORS ; Options to make maps playable despite serious flaws + Linedefs w/o tags apply locally ; If a linedef type that normally requires a tag (e.g. a + remote door) has no tag, then if possible apply the + linedef's action to the local sector. (Not available + for recording, playback or with demo_compatibility.) + Use passes thru all special lines ; Boom's passthru flag is applied to all special lines. + This makes it possible, for instance, to press switches + that have been placed behind special lines such as + scrolling textures. (Not available for recording, + playback or with demo_compatibility.) + Walk under solid hanging bodies ; Allows the player to pass under hanging objects in tall + sectors that would otherwise block movement. (Not available + for recording, playback or with demo_compatibility.) + +Additional mouse options in: +Main Menu\Options\Mouse sensitivity + + MOUSE LOOK ; Slider to set the sensitivity for mouselook (only applied with OpenGL rendering) + ACCELERATION ; Slider to set the level of mouse acceleration + +Additional command-line parameters: +"-solo-net" - for play, recording or playback using "single-player coop" mode. Equivalent to using prboom_server with -N 1 +"-window" - for temporarily using windowed mode without saving this in the cfg. +"-nowindow" - for temporarily using fullscreen mode without saving this in the cfg. +"-aspect NxM" - for using a different aspect ratio; e.g. -aspect 5x4, -aspect 8x5 or -aspect 2x1. +"-videodriver name" - for setting up the videodriver name that SDL will use. "windib" and "directx" are available for Windows. See SDL documentation for other platforms. "-videodriver default" can be used to force SDL behaviour by default. +"-pistolstart" - automatic pistol start when advancing from one level to the next +"-shorttics" - forces the same mouse behaviour as when recording (i.e. the converse of "-longtics") +"-stroller" - removes ability to strafe and limits running speed (a speedrun category) +"-resetgamma" - restores the original gamma after a crash +"-exe chex" - causes the game to play like chex.exe (Chex Quest) - chex.wad and chex.deh required too +"-geom NxM" [basic syntax] - for temporarily using a particular resolution without saving this in the cfg; e.g. -geom 1280x1024. +"-geom WidthxHeight[w|f]" [advanced syntax] - w - windowed, f - fullscreen. Examples: -geom 320x200f, -geom 640x480w + +Command-line parameters for playback of demos recorded with old versions that desynch due to recording bugs: +"-emulate [prboom_ver]" - for viewing old desynching Prboom demos with all recording bugs from a particular version emulated. For example, "-emulate 2.2.6" would be used to play back a desynching demo recorded with prboom 2.2.6. This would be instead of using the various command-line options for each recording bug (see below) from that version. +"-force_monster_avoid_hazards" - for viewing old desynching doom-compatible Prboom demos recorded with "monster_avoid_hazards 1" in config. +"-force_remove_slime_trails" - for viewing old desynching doom-compatible demos recorded with old versions of prboom (<2.4.6) or prboom-plus (< 2.4.6.1) that are affected by the "remove slime trails" incompatibility. +"-reject_pad_with_ff" - for viewing old desynching doom-compatible demos recorded with old versions of prboom (including 2.2.5 and 2.2.6) that padded a short REJECT with ones instead of zeroes. incompatibility. +"-force_truncated_sector_specials" - for emulation of old buggy behaviour whereby Boom's generalized effects could work instead of failing in a compatibility mode in the case of a bad action number. +"-force_no_dropoff" - for emulation of MBF_compatibility behaviour in prboom 2.2.2 - 2.4.7, whereby things did not drop off ledges when they would have done in MBF. +"-force_prboom_friction" - for emulation of old buggy behaviour whereby Boom's friction and bobbing code were not faithfully replicated. +"-force_force_boom_brainawake" - for emulation of Boom's behaviour with respect to the Monster Spawner, which differed slightly from that of "vanilla" Doom. +"-force_lxdoom_demo_compatibility" - for emulation of all bugs in demo compatibility mode in lxdoom. + +Additional and enhanced command-line parameters for demo playback: +"-warp x" - warps directly to the start of map x of a recording without rendering any of the play up to that point. Pressing Use ( by default) during skipping shows the current frame. +"-avidemo fileprefix" - grabbing of the screenshots to frmXXXX.tga +"-skipsec x" - skip X secs during viewing of the demo +"-warp x -skipsec y" will skip y seconds on level x. +"-recordfromto n.lmp m" - play back the demo n.lmp, allowing the user to take over the controls at a point of his choosing, and save the resulting demo as m.lmp +"-levelstat" - outputs a text-file called levelstat.txt containing level-by-level information on times, kills, items and secrets +"-spechit XXX" - provides a spechits magic number, overriding the program's default value +"-nomonsters" - for playback of -nomonsters demos recorded with Doom.exe 1.2 +"-respawn" - for playback of -respawn demos recorded with Doom.exe 1.2 +"-auto" - enables the autoloading of wads according to the lmp file-name. See below for information. +"-exe chex" - to playback chex.exe (Chex Quest) demos + +Additional command-line parameters for cheating/TAS functions: +"-trace_thingshealth ThingID [ThingID] [ThingID]" - displays the current health of the specified Things (in the HUD) +"-trace_thingspickup ThingID [ThingID] [ThingID]" - shows if the specified Things have been picked up (in the HUD) +"-trace_linescross LineID [LineID] [LineID]" - shows if the specified Lines have been crossed (in the HUD) +"-trace_givendamage ThingID [ThingID] [ThingID]" - shows the latest and total damage inflicted by any specific monsters or players (in the HUD) + +Examples: + glboom-plus -iwad doom2.wad -solo-net -playdemo 30fanet1 + + glboom-plus -iwad doom.wad -playdemo e1uv-405.lmp -warp 1 6 + glboom-plus -playdemo 30uv1441.lmp -warp 29 + + glboom-plus -playdemo 30uv1441.lmp -skipsec 192 + glboom-plus -iwad doom.wad -playdemo e1uv-405.lmp -skipsec 12.5 + + glboom-plus -timedemo e4uv-232.lmp -framescapture 1 frm + +(*1) See "record.bat" to understand how "record append" feature (-recordfromto) works + + +COMPATIBILITY LEVELS + +Demo recorders should read this carefully. These complevels all work both for recording and playback. +If you use a complevel for playback, it will override the autodetection of the demo type. This is useful to handle cases in which autodetection does not correctly identify the demo-type. Autodetection will still work for the vast majority of demos. + +Number Name Description + +0 doom_12_compatibility Partial (improved) emulation of Doom.exe v1.2 +1 doom_1666_compatibility Partial emulation of Doom.exe/Doom2.exe v1.666 +2 doom2_19_compatibility Emulates the original Doom.exe v1.9 & Doom2's doom2.exe v1.9 +3 ultdoom_compatibility Emulates Ultimate Doom v1.9 and Doom95 +4 finaldoom_compatibility Emulates Final Doom's doom2.exe +5 dosdoom_compatibility Emulates Dosdoom .47 +6 tasdoom_compatibility Emulates Tasdoom.exe +7 boom_compatibility_compatibility Emulates Boom's compatibility mode +8 boom_201_compatibility Emulates Boom v2.01 +9 boom_202_compatibility Emulates Boom v2.02 +10 lxdoom_1_compatibility Emulates LxDoom v1.4.x +11 mbf_compatibility Emulates MBF +12 prboom_1_compatibility Emulates PrBoom v2.03beta +13 prboom_2_compatibility Emulates PrBoom v2.1.0 +14 prboom_3_compatibility Emulates PrBoom v2.1.1-2.2.6 +15 prboom_4_compatibility Emulates PrBoom v2.3.x +16 prboom_5_compatibility Emulates PrBoom v2.4.0 +17 prboom_6_compatibility Latest PrBoom-plus +-1 default_compatibility_level Current Prboom-plus + +Here's a quick summary of the most important ones for recording: + +-complevel 2 - "vanilla" Doom2.exe +-complevel 3 - "vanilla" Ultimate Doom.exe +-complevel 9 - Boom +-complevel -1 - current Prboom+ + +Thus if you mostly want to record vanilla demos (and Boom when necessary), you can put 2 as your default compatibility for Doom2, 3 as the default compatibility for Ultimate Doom, and place -complevel 9 in the command line when recording on a Boom map. + +Example command lines: + +glboom-plus -complevel 2 -iwad doom.wad -file uac_dead.wad -playdemo uac_dead +This plays back a Doom 1.9 demo. It is necessary to force this complevel, as otherwise it would be autodetected as an Ultimate Doom demo (complevel 3), but this pwad doesn't function correctly with Ultimate Doom, and the demo would desync. + +glboom-plus -complevel 2 -iwad doom2.wad -file ksutra.wad -warp 15 -skill 5 -record kn15-xxx +This records a vanilla Doom2.exe demo + +glboom-plus -complevel 3 -iwad doom.wad -warp 1 9 -skill 5 -record n1m9-xxx +This records a vanilla Ultimate Doom.exe demo + +glboom-plus -solo-net -complevel 4 -iwad plutonia.wad -warp 32 -skill 5 -record pn32net1 +This records a single-player vanilla Final Doom demo using extra coop monsters & items, and with coop rules + +glboom-plus -complevel 5 -iwad doom2.wad -file icarus.wad -playdemo ic25uv +This plays back a Dosdoom .47 demo. It is necessary to force this complevel, as otherwise it would be autodetected as a Doom2.exe demo (complevel 2), which in this case would lead to a desync. + +glboom-plus -iwad doom2.wad -file nulspace.wad -playdemo nulnx415 +This plays back an unconverted Tasdoom.exe demo. There is no need to force complevel 6, as Tasdoom demos are autodetected. + +glboom-plus m223-005.lmp -auto +This plays back a demo and automatically loads the pwads that are required. Assuming the pattern-matching is correct and the wads are present, in this case it would load doom2.wad as the iwad, and mm2.wad and mm2mus.wad as pwads. + + +AUTOLOADING OF WADS ACCORDING TO THE LMP FILE-NAME + +The overall purpose of this feature is to enable demos to be watched by simply clicking on the lmp file - the program will load the necessary wads based on the lmp's file-name. +It does this by looking in a list of patterns in the cfg file. These patterns use Regular Expressions (RegExp). +While this cannot cater for all demos, a suitable list of patterns will enable a very large number of demos to be played back without the user needing to specify which wads are required. +Obviously, you need to have the wads unzipped and on your hard-disk in a place where the program will find them: the same directory as the exe, or in the %DOOMWADDIR%. + +If the launcher is enabled, and you have chosen to "Associate the current EXE with DOOM demos" (one of the options in "Commands:" at the bottom of the launcher window), +then clicking on the lmp file will bring up the launcher, with it displaying the files it believes are needed. + +Alternatively you can use the -auto command-line option. If you make... +glboom-plus "%1" -auto +...the default action for the lmp file-type, then clicking on a lmp will automatically play it back with the files loading according to the pattern matching. + +You may edit the list of patterns. The syntax is as in the following example: +demo_pattern24 "Memento Mori/((mm\d\d.\d\d\d)|(\d\dmm.\d\d\d)|(\d\d\d.mm\d\d)|(mm\d\d-uv)|(mm_...)|(mm[0-3]\d\D\D\D\D))\.lmp/doom2.wad|mm.wad|mmmus.wad" + +First comes a label identifying the pattern. Note that the numbering is (re)done automatically each time the program writes the cfg file - if you insert a new pattern, you do not need to edit all the later numbers by hand. The number is shown in the console to identify the pattern that has been matched. +Then comes a text description of the pattern. This will be displayed if the launcher is used to show which pattern has been matched. +Next comes the list of patterns (using Regular Expressions) to be matched. +Finally there is the list of files to be loaded. First is the iwad, and then the list of any additional pwads or deh files. + +Note that the program looks through the list of patterns sequentially. If a lmp's name matches pattern2 and also pattern20, then the wads for pattern2 will be the ones loaded. If the pattern is not matched at all, or any files are not found, a message is displayed. + +If you do significant editing of the patterns, be sure to back up your work. If you make a serious error in the patterns, you can find that the program has effectively deleted many of them when it next writes the cfg file. + +The cfg files in this package contain an up-to-date and very large set of patterns, which will suffice to play back almost all of the demos you will find online. These patterns will be updated over the course of time - see the Doomworld Demos Forum to get the very latest, or download the latest test version of prboom-plus, which will always include a recent set. For more information, see the documentation included in lmpwatch.zip in this package. + + +HI-RES TEXTURES, FLATS AND PATCHES + +If you wish to use hi-res textures and flats and/or patches, then you need to download a collection of them from somewhere. You need to extract the textures, flats and patches into suitably named subfolders of the folder where you have placed glboom-plus.exe, as follows: +/textures +/flats +/patches +Hopefully the package in which you have obtained the files will include the appropriate directory structure, so all you need to do is unzip it into the correct location with "Use folder names" (or an equivalent option) enabled. + + +VIDEO CAPTURE + +This feature allows you to record movie files, suitable for viewing or upload to websites. +Prboom-plus uses the external "ffmpeg" command line encoding tool to accomplish this. You may get a builds for Windows here: + https://ffmpeg.org/download.html#build-windows + +The application should be placed in the same folder as Prboom-plus. +Using the movie recording feature is simple. The command line parameter is "-viddump filename", and is meant to be used with -timedemo. +Example: + prboom-plus -timedemo 30uv1437 -viddump foo.mkv + +This will create foo.mkv playing demo 30uv1437.lmp. By default, foo.mkv will be an mkv container containing h264 video and opus audio, which most popular video sharing sites should understand. + +The recording uses all of your normal video and sound options, so make sure things like video resolution, gamma correction, audio samplerate, and sound volume are how you want them beforehand. Both OpenGL and software renderers are supported. Music recording will happen as long as you are not using the SDL or Portmidi music options. The video and sound are encoded as the demo is played, so the process can be slow even on a fast computer. But, regardless of how fast the encoding goes, the resulting file can playback at full speed with perfect sound sync and no dropped frames. Note that while you can watch the demo as it records, you will not be able to hear any sound. + + +VIDEO CAPTURE (ADVANCED) + +Unless you thoroughly understand video encoding, it's recommended you just use the default settings. However, if you are an encoding guru, you can control how Prboom-plus makes its videos through cfg options. These are the defaults: + +cap_soundcommand "ffmpeg -f s16le -ar %s -ac 2 -i - -c:a libopus -y temp_a.nut" +cap_videocommand "ffmpeg -f rawvideo -pix_fmt rgb24 -r %r -s %wx%h -i - -c:v libx264 -y temp_v.nut" +cap_muxcommand "ffmpeg -i temp_v.nut -i temp_a.nut -c copy -y %f" +cap_tempfile1 "temp_a.nut" +cap_tempfile2 "temp_v.nut" +cap_remove_tempfiles 1 + +You can use any command line encoders you want that can take the following input: +cap_soundcommand: Receives raw 16 bit PCM audio on stdin +cap_videocommand: Receives raw 24 bit RGB video frames on stdin + +A few simple substitutions are available in writing the command invocations: + %w video width in pixels + %h video height in pixels + %s sound rate in hertz + %f filename passed to -viddump + %% a single percent sign '%' + +cap_muxcommand is not given any special input and is run after the demo is complete. Finally, cap_tempfile1 and cap_tempfile2 are removed once the mux command is complete only if cap_remove_tempfiles is 1. + +For example, these were the previous defaults up to v2.5.1.7um, using the three separate external command line encoding tools "oggenc2", "x264" and "mkvmerge": + +cap_soundcommand "oggenc2 -r -R %s -q 5 - -o output.ogg" +cap_videocommand "x264 -o output.mp4 --crf 18 --muxer mp4 --demuxer raw --input-csp rgb --input-depth 8 --input-res %wx%h --fps %r -" +cap_muxcommand "mkvmerge -o %f output.mp4 output.ogg" +cap_tempfile1 "output.ogg" +cap_tempfile2 "output.mp4" +cap_remove_tempfiles 1 + +The console output from the various commands is redirected to file so you can see it for debugging purposes: + cap_soundcommand: + sound_stdout.txt + sound_stderr.txt + cap_videocommand: + video_stdout.txt + video_stderr.txt + cap_muxcommand: + mux_stdout.txt + mux_stderr.txt + diff --git a/doc/prboom-plus.6 b/doc/prboom-plus.6 new file mode 100644 index 0000000..f531ef1 --- /dev/null +++ b/doc/prboom-plus.6 @@ -0,0 +1,361 @@ +.TH PRBOOM-PLUS 6 "2011-06-27" +.SH NAME +prboom-plus \- PrBoom+, a version of Doom for Unix, Linux and Windows systems +.SH SYNOPSIS +.B prboom-plus +[options] [values|files] +.SH DESCRIPTION +.B PrBoom +is a version of the first-person shooter game Doom, originally released by iD software in 1993. +It is based on Boom, a version of Doom adapted by TeamTNT +(http://www.teamtnt.com/) for Dos. PrBoom uses the SDL library, +meaning it can run on a variety of different systems, +including Windows and Unix/X11. +.PP +.B PrBoom+ +is a Doom source port developed from the original PrBoom project. +It adds uncapped framerate, variable gamespeed, re-record, walkcam, chasecam, +full mouselook, FOV and other features without loss of compatibility +with original Doom. +.SH GAME OPTIONS +.TP +.BI \-complevel\ lvl +This sets the compatibility mode that PrBoom+ runs in. If you need to change +this, see \fBREADME.compat\fP. +.TP +\fB\-loadgame\fP { \fI0,1,2,3,4,5,6,7\fR } +Instructs PrBoom+ to load the specified saved game immediately. +.TP +\fB\-warp\fP { \fImap\fR | \fIepis\fR \fIlevel\fR } +Tells PrBoom+ to begin a new game immediately. For Doom 1 or The Ultimate Doom, +you must specify the \fIepis\fRode and \fIlevel\fR number to begin at +(\fIepis\fR is \fB1\fP for Knee-Deep in the Dead, \fB2\fP for Shores of Hell, +\fB3\fP for Inferno, \fB4\fP for Thy flesh Consumed; \fIlevel\fR is between +\fB1\fP and \fB9\fP). For Doom 2 or Final Doom, you must specify the +\fImap\fR to begin at, which is between \fB1\fP and \fB32\fP (\fB30\fP for +German Doom 2). +.TP +.BI \-skill\ n +Tells PrBoom+ to begin the game +at skill level \fIn\fR (\fB1\fP for I'm Too Young To Die; \fB2\fP for Hey, Not Too Rough; +\fB3\fP for Hurt Me Plenty; \fB4\fP for Ultra-Violent; \fB5\fP for Nightmare!). +.TP +.BI \-respawn +Tells PrBoom+ that monsters that die should respawn (come back to life) +after a while. Not for the inexperienced. +.TP +.BI \-fast +Tells PrBoom+ to make all the monsters move and +react faster. Not for the inexperienced. +.TP +.BI \-nomonsters +Tells PrBoom+ to include no monsters in the game. +.TP +.BI \-nocheats +Disable applying cheats from dehacked files. +.SH WAD OPTIONS +.TP +.BI \-iwad\ iwadname +Specifies the location of the IWAD file, typically doom.wad or doom2.wad +(or doom2f.wad). This tells PrBoom+ where the main .wad file that came +with the version of Doom that you own is. +.TP +.BI \-file\ wad1\ \&... +Specifies a list of PWAD files to load in addition to the IWAD file. PWAD +files modify the existing Doom game, by adding levels or new sounds or +graphics. PWAD files are widely available for download; try +http://www.doomworld.com/idgames/ for starters. +.TP +.BI \-deh\ deh_file +Tells PrBoom+ to load the dehacked patch \fIdeh_file\fR. +.SH DEMO (LMP) OPTIONS +.TP +.BI \-record\ demofile +Instructs PrBoom+ to begin recording a demo, to be stored in \fIdemofile.lmp\fR. +You should specify \fBgame options\fR to specify which level and skill +to record at. +.TP +.BI \-playdemo\ demofile +Play the recorded demo \fIdemofile.lmp\fR. +.TP +.BI \-timedemo\ demofile +Play the recorded demo \fIdemofile.lmp\fR, reporting information about +the length of the demo (in gametics) afterwards. +.TP +.BI \-viddump\ filename +Record a movie file, it requires external command-line encoding tools, +oggenc2 (ogg vorbis audio encoder), x264 (h264 video encoder) and +mkvmerge (mkv muxer). +It is meant to be used with \-timedemo, e.g. +"prboom-plus \-timedemo anydemo.lmp \-viddump filename.mkv". +.TP +.BI \-fastdemo\ demofile +Play the recorded demo \fIdemofile.lmp\fR as fast as possible. Useful for +benchmarking PrBoom+, as compared to other versions of Doom. +.TP +.BI \-ffmap\ num +Fast forward the demo (play at max speed) until reaching map \fInum\fR +(note that this takes just a number, not a map name, so so \fB-ffmap 7\fP +to go fast until MAP07 or ExM7). +.TP +.BI \-warp\ x +Warps directly to the start of map x of a recording without rendering any +of the play up to that point. Pressing Use ( by default) during +skipping shows the current frame. +.TP +.BI \-avidemo\ fileprefix +Grabbing of the screenshots to frmXXXX.tga. +.TP +.BI \-skipsec\ x +Skip X secs during viewing of the demo. +.TP +.BI \-warp\ x \ -skipsec\ y +Will skip y seconds on level x. +.TP +.BI \-recordfromto\ n.lmp\ m +Play back the demo n.lmp, allowing the user to take over the controls at +a point of his choosing, and save the resulting demo as m.lmp. +.TP +.BI \-levelstat +Outputs a text-file called levelstat.txt containing level-by-level information +on times, kills, items and secrets. +.TP +.BI \-spechit\ xxx +Provides a spechits magic number, overriding the program's default value. +.TP +.BI \-nomonsters +For playback of \-nomonsters demos recorded with Doom.exe 1.2. +.TP +.BI \-respawn +For playback of \-respawn demos recorded with Doom.exe 1.2. +.TP +.BI \-auto +Enables the autoloading of wads according to the lmp file-name. See below +for information. +.SS DEMOS OLD VERSIONS COMPATIBILITY PARAMETERS +.TP +.BI \-emulate\ prboom_ver +For viewing old desynching PrBoom demos with all recording bugs from a +particular version emulated. For example, "\-emulate 2.2.6" would be used +to play back a desynching demo recorded with PrBoom 2.2.6. This would be +instead of using the various command-line options for each recording bug +(see below) from that version. +.TP +.BI \-force_monster_avoid_hazards +For viewing old desynching doom-compatible PrBoom demos recorded with +"monster_avoid_hazards 1" in config. +.TP +.BI \-force_remove_slime_trails +For viewing old desynching doom-compatible demos recorded with old versions +of PrBoom (< 2.4.6) or PrBoom+ (< 2.4.6.1) that are affected by the +"remove slime trails" incompatibility. +.TP +.BI \-reject_pad_with_ff +For viewing old desynching doom-compatible demos recorded with old versions +of PrBoom (including 2.2.5 and 2.2.6) that padded a short REJECT with ones +instead of zeroes. +.TP +.BI \-force_truncated_sector_specials +For emulation of old buggy behaviour whereby Boom's generalized effects +could work instead of failing in a compatibility mode in the case of a +bad action number. +.TP +.BI \-force_no_dropoff +For emulation of MBF_compatibility behaviour in PrBoom 2.2.2 - 2.4.7, +whereby things did not drop off ledges when they would have done in MBF. +.TP +.BI \-force_prboom_friction +For emulation of old buggy behaviour whereby Boom's friction and bobbing +code were not faithfully replicated. +.TP +.BI \-force_force_boom_brainawake +For emulation of Boom's behaviour with respect to the Monster Spawner, +which differed slightly from that of "vanilla" Doom. +.TP +.BI \-force_lxdoom_demo_compatibility +For emulation of all bugs in demo compatibility mode in lxdoom. +.TP +.BI \-boom_deh_parser +Forces the Boom DEH parser. +.TP +.BI \-setmem\ system +The desynch in fez1-924.lmp @ fez1.wad is gone, but you still need to add +"\-setmem dosbox" or "\-setmem dos71" command line parameter, because the +default "dos622" memory layout causes a desynch. +.SH MULTIPLAYER OPTIONS +.TP +\fB\-net\fP \fIhostname\fR[:\fIport\fR] +Specifies that a TCP/IP network game is to be started. \fIhostname\fR is +the name of the machine on which the network game server is running +(\fBprboom-plus-game-server\fP). For more information about this, see +.BR prboom-plus-game-server (6) +and the \fBREADME\fP that came with PrBoom+. \fIport\fR is the +port number on the remote machine to which to connect; if not specified, +the default of \fB5030\fP (which is the default for +.BR prboom-plus-game-server (6) +) is assumed. +The server will configure your PrBoom+ settings, so that all the players +have the same game settings (skill, map etc). +.PP +Also, the server may specify additional PWAD files to play with; if you +do not have the required .WAD file, PrBoom+ will ask the server for a download +path, and attempt to use +.BR wget(1) +and if necessary +.BR unzip(1) +to download and extract the required WAD. +.TP +.BI \-port\ portnum +Specifies the local port to use to communicate with the server in a netgame. +.TP +.BI \-deathmatch +No longer used. Tells PrBoom+ to begin a deathmatch game, but this is +overridden by the server's settings. Only works for single play (!). +.TP +.BI \-altdeath +Similar to \fB\-deathmatch\fP, but implies a different set of rules for +the deathmatch game. No longer used (specified by the server). +.TP +.BI \-timer\ mins +No longer used. Specifies that levels will end after \fImins\fR minutes +of play if the level is still being played, but is overridden by the server +in a netgame. Not really useful for single play. +.TP +.BI \-avg +Equivalent to \fB-timer 20\fP. +.TP +.BI \-solo-net +Used to run a single-player network game, without a network game server. +This enables network game items & options for an otherwise single-player +game; some demos are recorded like this. +.SH VIDEO OPTIONS +.TP +.BI \-width\ w +Specifies the width of the PrBoom+ window, in pixels. Default is \fB320\fP, +the width must be greater than 320. +.TP +.BI \-height\ h +Specifies the height of the PrBoom+ window, in pixels. Default is \fB200\fP, +the height must be greater than 200. +.TP +.BI \-viewangle\ n +Causes the player view to be rotated by a given offset (specified in +45degree increments, in the range 0..7) from the way the player is facing. +.TP +.BI \-vidmode\ gl +Use the OpenGL video mode. The default is to use the software video mode. +.TP +.BI \-fullscreen,\ \-nofullscreen +These options toggle fullscreen mode. The default is fullscreen. +.TP +.BI \-window,\ \-nowindow +This pair of options also toggle fullscreen mode. They only take effect +for this PrBoom+ session and do not alter your configuration file. +.TP +.BI \-noaccel +For PrBoom+, this prevents it using the MITShm server extension for passing +the screen data to the X server. This option may be required if the X server +is not local. For lsdoom, this tells lsdoom not to use the accelerated +graphics functions that SVGALib provides even when they are supported for +your video card (normally this is autodetected). +.TP +.BR \-1 ,\ \-2 ,\ \-3 +Specifies the scale factor by which to enlarge the window. The default, +\fB-1\fP, displays the normal 320x200 pixel Doom screen (or whatever size +is specified by the \fB-width\fP and \fB-height\fP parameters or in the +config file for PrBoom+). If this window is too small, try using \fB-2\fP +or \fB-3\fP to enlarge the window. +.TP +.BI \-nodraw +Suppress all graphical display. Only for debugging & demo testing. +.TP +.BI \-aspect\ NxM +For using a different aspect ratio; e.g. \-aspect 5x4, \-aspect 8x5 or \-aspect 2x1. +.TP +.BI \-videodriver\ name +For setting up the videodriver name that SDL will use (See SDL documentation). +"\-videodriver default" can be used to force SDL behaviour by default. +.TP +.BI \-resetgamma +Restores the original gamma after a crash. +.TP +.BI \-geom\ NxM +Basic syntax, for temporarily using a particular resolution without saving +this in the cfg; e.g. \-geom 1280x1024. +.TP +.BI \-geom\ WidthxHeight[w|f] +Advanced syntax, w - windowed, f - fullscreen. Examples: \-geom 320x200f, +\-geom 640x480w. +.SH I/O OPTIONS +.TP +.BI \-nosound +Disables all sound effects and in-game music. This prevents the sound server +loading, which lets the game run a little faster. +.TP +.BI \-nosfx +Disables sound effects during the game. This does not stop the sound server +loading, however, so for best performance use \-nosound. +.TP +.BI \-nomusic +Disables playing of music in the game. +.TP +.BI \-nojoy +Disables joystick support. +.TP +.BI \-nomouse +Prevents the mouse being grabbed by the PrBoom+ window. +.TP +.BI \-shorttics +Forces the same mouse behaviour as when recording (i.e. the converse of +"\-longtics"). +.SH CONFIGURATION +.TP +.BI \-config\ myconf +Loads an alternative configuration file, named \fImyconf\fR. The default is +.BR prboom-plus.cfg (5), +taken from the same directory as PrBoom+ was run from. +.TP +.BI \-save\ savedir +Causes PrBoom+ to save games in the directory specified by \fIsavedir\fR +instead of \fB~/.prboom-plus/\fP. +.TP +.BI \-shotdir\ shotdir +Causes PrBoom+ to save screenshots in \fIshotdir\fR instead of the current +directory. +.SH DEBUGGING/PROFILING OPTIONS +.TP +.BI \-devparm +Development mode. Mostly redundant these days, but it does force non-lazy +generation of texture lookups which can be useful for level authors debugging +PWADs. +.TP +.BI \-debugfile\ debug_file +Causes some debugging information, mainly network info, to be +written to the named file as PrBoom+ runs. +.TP +.BI \-nodrawers +Causes no rendering to be done. The only conceivable use of this is (a) +a multiplayer server (b) to test the speed of the other routines in the +program, when combined with \fB\-timedemo\fP. +.TP +.BI \-noblit +Causes no copying to the screen from the rendering buffer to be performed. +The only conceivable use of this is (a) a multiplayer server (b) to test +the speed of the other routines in the program, when combined with \fB\-timedemo\fP. +.TP +.BI \-bexout\ bexdbg +Causes diagnostics related to bex and dehacked file processing to be written +to the names file. +.TP +.BI \-blockmap +Use if PrBoom-Plus reports a buggy blockmap. +.SH SEE ALSO +.BR prboom-plus.cfg (5), +.BR prboom-plus-game-server (6) +.PP +For more information, see the \fBREADME\fP that came with PrBoom+, the Boom +documentation, and your original Doom documentation. +.PP +Doom is a registered trademark of id software (http://www.idsoftware.com/). +.SH AUTHORS +See the file \fBAUTHORS\fP included with the PrBoom+ distribution. diff --git a/doc/prboom-plus.cfg.5 b/doc/prboom-plus.cfg.5 new file mode 100644 index 0000000..1e0d8d6 --- /dev/null +++ b/doc/prboom-plus.cfg.5 @@ -0,0 +1,348 @@ +.TH PRBOOM-PLUS.CFG 5 "2011-06-27" +.SH NAME +prboom-plus.cfg \- Configuration file for PrBoom+ +.SH USAGE +When a version of PrBoom+ is +run, it searches for this configuration file to modify its default settings. +Every time PrBoom+ exits, it rewrites the configuration file, updating any +settings that have been changed using the in-game menus. +.PP +PrBoom+ expects the config file to be \fB~/.prboom-plus/prboom-plus.cfg\fP. +Alternatively, it can be made to look elsewhere by using a command-line +parameter: +.PP +.B prboom-plus +.RB [\| \-config +.IR myconf \|] +.RB +.SH FORMAT +\fBprboom-plus.cfg\fP consists of a number of variables and values. Each +line is of the following format: +.PP +{ +{{# | ; | [} +.I comment_text} +| { +.I variable +{\fIdecimal_integer\fR | 0x\fIhex_integer\fR | "\fIstring_text\fR"}} +} +.PP +Any line beginning with a non-alphabetic character is treated as a comment +and ignored; for future compatibility you should start comments with a +\fB#\fP, \fB;\fP or \fB[\fP. +Note however that when PrBoom+ rewrites prboom-plus.cfg it does not preserve user +added comments. +.PP +Any line beginning with an alphabetic character is treated as a variable-value +pair. +The first word (sequence of non-whitespace characters) is the variable name, +and everything after the following block of whitespace is taken to be the +value assigned to the variable. +.PP +Variables not recognised by PrBoom+, or which are given an invalid value +or a value of an inappropriate type, are ignored. Warning messages are +given where relevant. +.PP +The variables recognised by PrBoom+ are described per-section in the following +sections. The sections are informal however; when PrBoom+ rewrites the +config file it writes in section headings and puts variables into the relevant +sections, but when reading these are ignored. +.SH MISC SETTINGS +.TP +.B compatibility_level +PrBoom+ is capable of behaving in a way compatible with earlier versions +of Doom and Boom/PrBoom[+]. The value given here selects the version to be +compatible with when doing new games/demos. See \fBREADME.compat\fP for +details. +.TP +.B realtic_clock_rate +Selects the speed that PrBoom+ runs at, as a percentage of normal game speed. +Leave at \fB0\fP unless you want to experiment. Note that it is considered +`cheating' to use this at any setting below or above \fB0\fP. +.TP +.B max_player_corpse +Sets the maximum number of player corpses to leave lying around. If this +limit would be exceeded, an old corpse is removed. Useful for big/long +Deathmatch games, where the sheer number of corpses could slow the game +down. +.TP +.B flashing_hom +Flag indicating whether a flashing red background is drawn to highlight +HOM (Hall of Mirrors) errors in levels (for level developers). +.TP +.B demo_insurance +Selects a method of protecting demos against `going out of sync' (where +the player seems to lose control and behave madly, but in fact the players +original instructions as stored in the demo have got out of sync with the +game he was playing). \fB0\fP=No protection, \fB1\fP=Full protection, +\fB2\fP=Only while recording demos. Safest when left set to \fB2\fP. +.TP +.B endoom_mode +This parameter specifies options controlling the display of the credits +screen when Doom exits. Currently it is the sum of 3 options: add \fB1\fP +for colours, \fB2\fP for non-ASCII characters to be displayed, and \fB4\fP +for the last line to be skipped so the top line doesn't scroll off screen. +.TP +.B level_precache +If set, when loading a new level PrBoom+ precaches all the graphics the +level is likely to need in memory. This makes it much slower to load the +level, but reduces disk activity and slowdowns reading data during play. +Most systems are fast enough that precaching is not needed. +.SH FILES SETTINGS +.TP +.BR wadfile_1,\ \fBwadfile_2\fP +The names of 2 .wad files to be automatically loaded when PrBoom+ is started. +A blank string means unused. +.TP +.BR dehfile_1,\ \fBdehfile_2\fP +The names of 2 patch files (.deh or .bex) to be automatically loaded when +PrBoom+ is started (empty string for none). +.SH GAME SETTINGS +.TP +.B default_skill +The default skill level when starting a new game. +.TP +.B weapon_recoil +Enables recoil from weapon fire. +.TP +.B doom_weapon_toggles +Flag indicating whether pressing 3 or 1 when that weapon is already selected +causes the selected shotgun or fist/chainsaw to be toggled, as in original +Doom. Some people prefer to use a number for each weapon alone. +.TP +.B player_bobbing +Enables player bobbing (view moving up/down slightly as the player +runs). +.TP +.B monsters_remember +Makes monsters remember their previous enemy after killing their current +target. +.TP +.B monster_infighting +Whether monsters will fight each other when they injure each other +accidentally. +.TP +.B monster_backing +Whether monsters without close combat weapons will back away from close +combat (unlike original Doom). +.TP +.B monster_avoid_hazards +Whether monsters avoid crushing ceilings. +.TP +.B monkeys +Whether monsters will climb steep stairs. +.TP +.B monster_friction +Whether monsters are affected by changed floor friction (they should be, +but weren't in Boom). +.TP +.B help_friends +Whether monsters will help out injured monsters by aiding them against +their attacker. +.TP +.B player_helpers +The number of helper dogs to spawn. +.TP +.B friend_distance +Distance within which friends will generally stay. +.TP +.B dog_jumping +Whether dogs will jump. +.TP +.B sts_always_red +PrBoom+ can make the colour of the text displays on the status bar reflect +your current status (\fBred\fP=low, \fByellow\fP=average, \fBgreen\fP=good, +\fBblue\fP=super-charged). This option if set selects the traditional +Doom behavior of always-red status bar display; set to \fB0\fP to allow +the coloured display. +.TP +.B sts_pct_always_gray +See above, this makes just the percent signs always gray, instead of +changing colour. +.TP +.B sts_traditional_keys +Doom and Boom have two types of keys; PrBoom+ will normally display both +keys of a given colour if you have both. This option, if enabled, instead +makes PrBoom+ only ever display one key of each colour, in the same way +Doom did. +.TP +.B traditional_menu +Changes PrBoom+'s menu ordering to be the same as original Doom if enabled. +.TP +.B show_messages +When enabled, text messages are displayed in the top left corner of the +screen describing events in the game. Can be toggled in the game, this is +just to preserve the setting. +.TP +.B autorun +Makes the player always run, without having to hold down a run key. Can +be toggled in the game, this just preserves the setting. +.SH SOUND SETTINGS +.TP +.B sound_card +Selects whether sound effects are enabled (non-zero enables). For compatibility +reasons with Boom, a range of values are accepted. +.TP +.B music_card +Selects whether in-game music is enabled (non-zero enables). For compatibility +reasons a range of values are accepted. +.TP +.B pitched_sounds +If enabled by this variable, this enables `pitching' (making pitch adjustments +to the playing sounds) for 16 bit sound cards. +.TP +.B samplerate +The samplerate for soundmixing and timidity. The sound quality is much +better at higher samplerates, but if you use timidity then higher samplerates +need much more CPU power. Useful values are \fB11025\fP, \fB22050\fP, +\fB44100\fP and \fB48000\fP. +.TP +.B sfx_volume +Sound effects volume. This is best adjusted in the game. +.TP +.B music_volume +Music volume. This is best adjusted in the game. +.TP +.B mus_pause_opt +Selects what PrBoom+ does to the music when a games is paused. \fB0\fP=stop +the music, \fB1\fP=pause the music (stop it playing, but when resumed resume +it at the same place - not implemented), \fB2\fP=continue playing. +.TP +.BR sounddev ,\ snd_channels ,\ soundsrv ,\ musicsrv +These variables are no longer used by PrBoom+, but are kept for compatibility +reasons. +.SH COMPATIBILITY SETTINGS +These are settings that let you choose whether the normal game mechanics +are used, or whether various quirks, bugs and limitations of the original +Doom game are emulated. +.SH VIDEO SETTINGS +.TP +.BR screen_width ,\ screen_height +For versions of PrBoom+ which support high-res, these specify the default +screen or window size for PrBoom+. These settings are ignored and preserved by +versions of PrBoom+ which do not do high-res (they assume 320x200). +.TP +.B use_fullscreen +If set, this causes PrBoom+ to try to go full screen. Depending on your +video driver and mode, this may include changing screen resolution to +better match the game's screen resolution. +.TP +.B use_doublebuffer +Use double buffering to reduce tearing. On some machines this is even faster +than the normal method, but on others this makes problems, so you have to +try out which setting works best. +.TP +.B translucency +Causes PrBoom+ to display certain objects as translucent. +.TP +.B tran_filter_pct +Selects how translucent objects are when they are translucent. Play with +this and see for yourself. +.TP +.B screenblocks +Selects a reduced screen size inside the PrBoom+ window (the player's view +is surrounded by a border). Normally this is undesirable, but it can help +speed up the game. Can be changed in the game with the +/- keys, this +variable is just to preserve that setting. +.TP +.B usegamma +Selects a level of gamma correction (extra screen brightening) to correct +for a dark monitor or light surroundings. Can be selected in the game with +the F11 key, this config entry preserves that setting. +.SH OPENGL SETTINGS +.PP +If you are knowledgeable about OpenGL, you can tweak various aspects of +the GL rendering engine. +.TP +.B gl_nearclip +The near clipping plane *100. +.TP +.B gl_colorbuffer_bits +The bit depth for the framebuffer. (\fB16\fP, \fB24\fP or \fB32\fP bits). +.TP +.B gl_depthbuffer_bits +The bit depth for the z-buffer. (\fB16\fP, \fB24\fP or \fB32\fP bits). +.TP +.B gl_tex_filter_string +A string, one of the following: \fBGL_NEAREST\fP or \fBGL_LINEAR\fP +(no mipmapping), or one of +\fBGL_NEAREST_MIPMAP_NEAREST\fP, \fBGL_NEAREST_MIPMAP_LINEAR\fP, +\fBGL_LINEAR_MIPMAP_NEAREST\fP, \fBGL_LINEAR_MIPMAP_LINEAR\fP +with mipmapping. +.TP +.B gl_tex_format_string +One of the following strings: +\fBGL_RGBA\fP - means format selected by driver (not so good), +\fBGL_RGBA2\fP - means 2 bits for each component (bad), +\fBGL_RGBA4\fP - means 4 bits for each component (like \fBGL_RGBA\fP on +most cards), +\fBGL_RGB5_A1\fP - means 5 bits for each color component 1 bit for the +alpha channel (default), +\fBGL_RGBA8\fP - means 8 bits for each component (best quality, but only +a little bit better than \fBGL_RGB5_A1\fP and slower on most cards). +.TP +.B gl_drawskys +If \fB0\fP, disables drawing skies, which may be needed with some problematic +3D cards. +.TP +.B gl_sortsprites +Experimental option, possibly faster but less reliable. +.SH MOUSE SETTINGS +.PP +This section specifies settings for using a mouse with PrBoom+. There are +several settings that control button bindings (what action each button +causes in the game); these are easiest set from the in-game menus, these +config entries are to preserve the settings between games. +.TP +.B use_mouse +Enable or disable the use of a mouse with PrBoom+. +.TP +.BR mouse_sensitivity_horiz ,\ mouse_sensitivity_vert +Sets the sensitivity of the mouse in PrBoom+. Easier set from the in-game +menus. +.SH KEY BINDINGS +.PP +These specify the keys that trigger various actions in PrBoom+. The codes +used for keys are internal to PrBoom+, though many keys are represented +by their ASCII codes. It is easiest to modify these via the in-game menus +(OPTIONS->SETUP->KEY BINDINGS). These config file entries preserve the +settings from this menu between game sessions. +.SH JOYSTICK SETTINGS +.PP +There are the trigger variables here, which are calculated during joystick +calibration (the values received from the kernel driver outside of which +movement is caused in the game). Also there are the button-bindings, again +best adjusted using the in-game menus. +.TP +.B use_joystick +This selects the number of the joystick to use, or \fB0\fP selects no joystick. +You have to have the relevant device files (\fB/dev/js0\fP etc) and the +kernel driver loaded. +.SH CHAT MACROS +.PP +These are pre-written text strings for quick transmission to players in a +network game (consult your Doom documentation). Easiest set via the in-game +menus (OPTIONS->SETUP->CHAT MACROS). +.SH AUTOMAP SETTINGS +.PP +These are settings related to the automap. These are easiest set from +within the game. +.SH HEADS_UP DISPLAY SETTINGS +.PP +These are settings related to the heads-up display, that is messages received +while playing and the heads-up display of your current status obtained by +pressing + while the view is full-screen in PrBoom+. See the Boom documentation +for details. All controlled best from within the game. +.SH WEAPON PREFERENCES +.PP +Here are the settings from the Weapons menu in the game +(OPTIONS->SETUP->WEAPONS). +.SH SEE ALSO +.BR prboom-plus (6), +.BR prboom-plus-game-server (6), +PrBoom+'s documentation (including the Boom and MBF documentation) +and your Doom documentation. +.SH AUTHORS +See the file \fBAUTHORS\fP included with PrBoom+ for a list of contributors +to PrBoom+. +This config file reference written by Colin Phipps (cph@moria.org.uk). diff --git a/doc/umapinfo.txt b/doc/umapinfo.txt new file mode 100644 index 0000000..2a3823c --- /dev/null +++ b/doc/umapinfo.txt @@ -0,0 +1,2 @@ +Document moved here: +https://github.com/kraflab/umapinfo diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..279f4af --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,524 @@ + +function(ApplySDL2NetOptions TARGET) + if(SDL2_NET_FOUND) + target_include_directories(${TARGET} PRIVATE ${SDL2_NET_INCLUDE_DIRS}) + target_link_libraries(${TARGET} PRIVATE ${SDL2_NET_LIBRARIES}) + endif() +endfunction() + +# PrBoom main executable + +set(COMMON_SRC + am_map.c + am_map.h + doomdata.h + doomdef.c + doomdef.h + doomstat.c + doomstat.h + doomtype.h + dstrings.c + dstrings.h + d_deh.c + d_deh.h + d_englsh.h + d_event.h + d_items.c + d_items.h + d_main.c + d_main.h + d_net.h + d_player.h + d_think.h + d_ticcmd.h + e6y.c + e6y.h + f_finale.c + f_finale.h + f_finale2.c + f_wipe.c + f_wipe.h + g_game.c + g_game.h + g_overflow.c + g_overflow.h + hu_lib.c + hu_lib.h + hu_stuff.c + hu_stuff.h + hu_tracers.c + hu_tracers.h + info.c + info.h + i_capture.c + i_capture.h + i_glob.c + i_glob.h + i_joy.h + i_main.h + i_network.h + i_pcsound.c + i_pcsound.h + i_sound.h + i_system.h + i_video.h + lprintf.c + lprintf.h + md5.c + md5.h + m_argv.c + m_argv.h + m_bbox.c + m_bbox.h + m_cheat.c + m_cheat.h + m_fixed.h + m_menu.c + m_menu.h + m_misc.c + m_misc.h + m_io.c + m_io.h + m_random.c + m_random.h + m_swap.h + protocol.h + p_ceilng.c + p_checksum.c + p_checksum.h + p_doors.c + p_enemy.c + p_enemy.h + p_floor.c + p_genlin.c + p_inter.c + p_inter.h + p_lights.c + p_map.c + p_map.h + p_maputl.c + p_maputl.h + p_mobj.c + p_mobj.h + p_plats.c + p_pspr.c + p_pspr.h + p_saveg.c + p_saveg.h + p_setup.c + p_setup.h + p_sight.c + p_spec.c + p_spec.h + p_switch.c + p_telept.c + p_tick.c + p_tick.h + p_user.c + p_user.h + r_bsp.c + r_bsp.h + r_data.c + r_data.h + r_defs.h + r_demo.c + r_demo.h + r_draw.c + r_draw.h + r_filter.c + r_filter.h + r_fps.c + r_fps.h + r_main.c + r_main.h + r_patch.c + r_patch.h + r_plane.c + r_plane.h + r_segs.c + r_segs.h + r_sky.c + r_sky.h + r_state.h + r_things.c + r_things.h + scanner.cpp + scanner.h + sc_man.c + sc_man.h + sounds.c + sounds.h + statdump.c + statdump.h + st_lib.c + st_lib.h + st_stuff.c + st_stuff.h + s_advsound.c + s_advsound.h + s_sound.c + s_sound.h + tables.c + tables.h + umapinfo.cpp + umapinfo.h + version.c + version.h + v_video.c + v_video.h + wi_stuff.c + wi_stuff.h + w_wad.c + w_wad.h + z_bmalloc.c + z_bmalloc.h + z_zone.c + z_zone.h +) + +set(NET_CLIENT_SRC + d_client.c +) + +if(HAVE_MMAP OR HAVE_CREATE_FILE_MAPPING) + set(WAD_SRC w_mmap.c) +else() + set(WAD_SRC w_memcache.c) +endif() + +set(MUS2MID_SRC + memio.c + memio.h + mus2mid.c + mus2mid.h +) + +set(SDLDOOM_SOURCES + SDL/i_joy.c + SDL/i_main.c + SDL/i_network.c + SDL/i_sound.c + SDL/i_sshot.c + SDL/i_system.c + SDL/i_video.c +) + +set(PCSOUND_SOURCES + PCSOUND/pcsound.c + PCSOUND/pcsound.h + PCSOUND/pcsound_linux.c + PCSOUND/pcsound_sdl.c + PCSOUND/pcsound_win32.c +) + +set(TEXTSCREEN_SOURCES + TEXTSCREEN/doomkeys.h + TEXTSCREEN/txt_main.h + TEXTSCREEN/txt_font.h + TEXTSCREEN/txt_largefont.h + TEXTSCREEN/txt_sdl.c + TEXTSCREEN/txt_sdl.h + TEXTSCREEN/txt_smallfont.h +) + +set(DOOMMUSIC_SOURCES + MUSIC/dbopl.c + MUSIC/dbopl.h + MUSIC/dumbplayer.c + MUSIC/dumbplayer.h + MUSIC/flplayer.c + MUSIC/flplayer.h + MUSIC/madplayer.c + MUSIC/madplayer.h + MUSIC/midifile.c + MUSIC/midifile.h + MUSIC/musicplayer.h + MUSIC/opl.c + MUSIC/opl.h + MUSIC/oplplayer.c + MUSIC/oplplayer.h + MUSIC/opl_queue.c + MUSIC/opl_queue.h + MUSIC/portmidiplayer.c + MUSIC/portmidiplayer.h + MUSIC/alsaplayer.c + MUSIC/alsaplayer.h + MUSIC/vorbisplayer.c + MUSIC/vorbisplayer.h +) + +set(EXTRA_FILES + r_drawcolpipeline.inl + r_drawcolumn.inl + r_drawflush.inl + r_drawspan.inl +) + +set(PRBOOM_PLUS_SOURCES + ${COMMON_SRC} + ${NET_CLIENT_SRC} + ${WAD_SRC} + ${MUS2MID_SRC} + ${SDLDOOM_SOURCES} + ${PCSOUND_SOURCES} + ${TEXTSCREEN_SOURCES} + ${DOOMMUSIC_SOURCES} + ${EXTRA_FILES} +) + +function(AddGameExecutable TARGET SOURCES) + if(OPENGL_FOUND AND OPENGL_GLU_FOUND) + set(SOURCES + ${SOURCES} + gl_clipper.c + gl_detail.c + gl_drawinfo.c + gl_fbo.c + gl_gamma.c + gl_hires.c + gl_hqresize.c + gl_intern.h + gl_light.c + gl_main.c + gl_map.c + gl_missingtexture.c + gl_opengl.c + gl_opengl.h + gl_preprocess.c + gl_shader.c + gl_shadow.c + gl_sky.c + gl_struct.h + gl_texture.c + gl_vertex.c + gl_wipe.c + ) + endif() + + if(WIN32) + add_definitions("-DUSE_WIN32_PCSOUND_DRIVER -DUSE_WINDOWS_LAUNCHER") + set(SOURCES + ${SOURCES} + ../ICONS/icons.rc + e6y_launcher.c + e6y_launcher.h + SDL/SDL_windows_main.c + ) + if(MSVC) + set(SOURCES + ${SOURCES} + WIN/win_opendir.c + WIN/win_opendir.h + ) + endif() + endif() + + add_definitions("-DUSE_EXPERIMENTAL_MUSIC") + + add_executable(${TARGET} WIN32 ${SOURCES}) + target_include_directories(${TARGET} PRIVATE + ${SDL2_INCLUDE_DIRS} + ${CMAKE_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_link_libraries(${TARGET} PRIVATE + ${SDL2_LIBRARIES} + ) + if(WIN32) + target_link_libraries(${TARGET} PRIVATE + winmm + comctl32 + ) + endif() + set_target_properties(${TARGET} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${PRBOOM_OUTPUT_PATH} + ) + if(SDL2_IMAGE_FOUND) + target_include_directories(${TARGET} PRIVATE ${SDL2_IMAGE_INCLUDE_DIRS}) + target_link_libraries(${TARGET} PRIVATE ${SDL2_IMAGE_LIBRARIES}) + endif() + if(SDL2_MIXER_FOUND) + target_include_directories(${TARGET} PRIVATE ${SDL2_MIXER_INCLUDE_DIRS}) + target_link_libraries(${TARGET} PRIVATE ${SDL2_MIXER_LIBRARIES}) + endif() + if(PCREPOSIX_FOUND) + target_include_directories(${TARGET} PRIVATE ${PCRE_INCLUDE_DIR}) + target_link_libraries(${TARGET} PRIVATE ${PCREPOSIX_LIBRARIES}) + endif() + ApplySDL2NetOptions(${TARGET}) + if(ZLIB_FOUND) + target_include_directories(${TARGET} PRIVATE ${ZLIB_INCLUDE_DIRS}) + target_link_libraries(${TARGET} PRIVATE ${ZLIB_LIBRARIES}) + endif() + if(LIBMAD_FOUND) + target_include_directories(${TARGET} PRIVATE ${LIBMAD_INCLUDE_DIRS}) + target_link_libraries(${TARGET} PRIVATE ${LIBMAD_LIBRARIES}) + endif() + if(FLUIDSYNTH_FOUND) + target_include_directories(${TARGET} PRIVATE ${FLUIDSYNTH_INCLUDE_DIR}) + target_link_libraries(${TARGET} PRIVATE ${FLUIDSYNTH_LIBRARIES}) + endif() + if(DUMB_FOUND) + target_include_directories(${TARGET} PRIVATE ${DUMB_INCLUDE_DIR}) + target_link_libraries(${TARGET} PRIVATE ${DUMB_LIBRARY}) + endif() + if(VORBIS_FOUND) + target_include_directories(${TARGET} PRIVATE ${VORBIS_INCLUDE_DIR}) + target_link_libraries(${TARGET} PRIVATE ${VORBISFILE_LIBRARY}) + endif() + if(PortMidi_FOUND) + target_include_directories(${TARGET} PRIVATE ${PORTMIDI_INCLUDE_DIR}) + target_link_libraries(${TARGET} PRIVATE ${PORTMIDI_LIBRARIES}) + endif() + if(ALSA_FOUND) + target_include_directories(${TARGET} PRIVATE ${ALSA_INCLUDE_DIR}) + target_link_libraries(${TARGET} PRIVATE ${ASOUND_LIBRARY}) + endif() + add_dependencies(${TARGET} prboomwad) + + if(MSVC) + set_target_properties(${TARGET} PROPERTIES + LINK_FLAGS "/MANIFEST:NO" + ) + add_custom_command(TARGET ${TARGET} POST_BUILD + COMMAND "mt.exe" -manifest \"${CMAKE_CURRENT_SOURCE_DIR}\\..\\ICONS\\prboom-plus.exe.manifest\" -outputresource:\"$\"\;\#1 + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${WAD_DATA_PATH} $ + ) + endif() + + install(TARGETS ${TARGET} COMPONENT "Game executable" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") + + if(OPENGL_FOUND AND OPENGL_GLU_FOUND) + target_compile_definitions(${TARGET} PRIVATE + GL_DOOM + USE_SHADERS + USE_FBO_TECHNIQUE + USE_GLU_IMAGESCALE + USE_GLU_MIPMAP + USE_GLU_TESS + ) + target_link_libraries(${TARGET} PRIVATE + ${OPENGL_gl_LIBRARY} + ${OPENGL_glu_LIBRARY} + ) + endif() +endfunction() + +AddGameExecutable(prboom-plus "${PRBOOM_PLUS_SOURCES}") + + +# PrBoom-Plus server executable + +option(BUILD_SERVER "Build PrBoom-Plus server executable" ON) + +if(BUILD_SERVER AND SDL2_NET_FOUND) + set(PRBOOM_PLUS_GAME_SERVER_SOURCES + d_server.c + protocol.h + SDL/i_network.c + ) + if(MSVC) + set(PRBOOM_PLUS_GAME_SERVER_SOURCES + ${PRBOOM_PLUS_GAME_SERVER_SOURCES} + SDL/i_system.c + ) + else() + set(PRBOOM_PLUS_GAME_SERVER_SOURCES + ${PRBOOM_PLUS_GAME_SERVER_SOURCES} + POSIX/i_system.c + ) + endif() + + add_executable(prboom-plus-game-server ${PRBOOM_PLUS_GAME_SERVER_SOURCES}) + target_include_directories(prboom-plus-game-server PRIVATE + ${SDL2_INCLUDE_DIRS} + ${CMAKE_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_link_libraries(prboom-plus-game-server PRIVATE + ${SDL2_LIBRARIES} + ) + set_target_properties(prboom-plus-game-server PROPERTIES + COMPILE_DEFINITIONS PRBOOM_SERVER + RUNTIME_OUTPUT_DIRECTORY ${PRBOOM_OUTPUT_PATH} + ) + ApplySDL2NetOptions(prboom-plus-game-server) + install(TARGETS prboom-plus-game-server COMPONENT "Game server executable" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +endif() + + +# PrBoom-Plus macOS launcher + +if(APPLE) + set(LAUNCHER_SOURCES + MAC/ANSIString.m + MAC/ConsoleController.h + MAC/ConsoleController.m + MAC/DrawerButton.h + MAC/DrawerButton.m + MAC/FileButtonController.h + MAC/FileButtonController.m + MAC/LauncherApp.h + MAC/LauncherApp.m + MAC/LauncherMain.m + MAC/ResolutionDataSource.h + MAC/ResolutionDataSource.m + MAC/UKFileWatcher.h + MAC/UKKQueue.h + MAC/UKKQueue.m + MAC/UKMainThreadProxy.h + MAC/UKMainThreadProxy.m + MAC/WadViewController.h + MAC/WadViewController.m + ) + + set(LAUNCHER_RESOURCES + MAC/Launcher.icns + MAC/PrBoom.icns + MAC/PrBoom.sdef + ) + set_source_files_properties(${LAUNCHER_RESOURCES} PROPERTIES + MACOSX_PACKAGE_LOCATION Resources + ) + + set(LAUNCHER_MAINMENU_RESOURCES + MAC/English.lproj/MainMenu.nib/classes.nib + MAC/English.lproj/MainMenu.nib/info.nib + MAC/English.lproj/MainMenu.nib/keyedobjects.nib + ) + set_source_files_properties(${LAUNCHER_MAINMENU_RESOURCES} PROPERTIES + MACOSX_PACKAGE_LOCATION Resources/English.lproj/MainMenu.nib + ) + + add_executable(prboom-plus-launcher MACOSX_BUNDLE + ${LAUNCHER_SOURCES} + ${LAUNCHER_RESOURCES} + ${LAUNCHER_MAINMENU_RESOURCES} + ) + target_include_directories(prboom-plus-launcher PRIVATE + ${CMAKE_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ) + set_target_properties(prboom-plus-launcher PROPERTIES + LINK_FLAGS "-framework Cocoa" + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/MAC/Info.plist" + OUTPUT_NAME "Launcher" + RUNTIME_OUTPUT_DIRECTORY ${PRBOOM_OUTPUT_PATH} + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "" + ) + add_dependencies(prboom-plus-launcher prboom-plus) + + set(BUNDLE_GAME_EXECUTABLE prboom-plus) + + add_custom_command(TARGET prboom-plus-launcher POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $/PrBoom-Plus + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${WAD_DATA_PATH} $ + ) + + if(BUILD_SERVER AND SDL2_NET_FOUND) + add_dependencies(prboom-plus-launcher prboom-plus-game-server) + add_custom_command(TARGET prboom-plus-launcher POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ + ) + endif() +endif() diff --git a/src/MAC/ANSIString.h b/src/MAC/ANSIString.h new file mode 100644 index 0000000..5f6a86b --- /dev/null +++ b/src/MAC/ANSIString.h @@ -0,0 +1,30 @@ +// Copyright (C) 2006 Neil Stevens +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name(s) of the author(s) shall not be +// used in advertising or otherwise to promote the sale, use or other dealings +// in this Software without prior written authorization from the author(s). + +#import + +@interface ANSIString : NSObject + ++ (NSAttributedString *)parseColorCodes:(NSString *)ansiString; + +@end diff --git a/src/MAC/ANSIString.m b/src/MAC/ANSIString.m new file mode 100644 index 0000000..3d1ef9a --- /dev/null +++ b/src/MAC/ANSIString.m @@ -0,0 +1,223 @@ +// Copyright (C) 2006 Neil Stevens +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name(s) of the author(s) shall not be +// used in advertising or otherwise to promote the sale, use or other dealings +// in this Software without prior written authorization from the author(s). + +#import "ANSIString.h" + +@implementation ANSIString + +static NSUInteger findNext(NSString *string, NSString *needle, NSUInteger start) +{ + if(start >= [string length]) + { + return NSNotFound; + } + else + { + NSUInteger i = [string rangeOfString:needle options:0 + range:NSMakeRange(start, [string length] - start)].location; + return i; + } +} + +static NSFont *defaultFont(void) +{ + return [[[NSFont userFixedPitchFontOfSize:12] retain] autorelease]; +} + +static NSColor *ansiColor(int c, int bold) +{ + float value = bold ? 1.0 : 0.5; + float a = 1.0; + float r = 0.0; + float g = 0.0; + float b = 0.0; + if(c & 1) + r = value; + if(c & 2) + g = value; + if(c & 4) + b = value; + + NSColor *color = [NSColor colorWithCalibratedRed:r green:g blue:b alpha:a]; + return [[color retain] autorelease]; +} + +static NSDictionary *attributes(bool bold, bool blink, bool reverse, int bg, int fg, int isReset) +{ + // TODO: implement blinking? + + int bgColor = bg; + int fgColor = fg; + int bgBold = false; + if(reverse) + { + bgColor = fg; + fgColor = bg; + } + // Special case hackery + // Make the reset case be black on white + if(isReset) + { + fgColor = 0; + bgColor = 7; + bgBold = true; + } + + NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys: + defaultFont(), NSFontAttributeName, + ansiColor(fgColor, bold), NSForegroundColorAttributeName, + ansiColor(bgColor, bgBold), NSBackgroundColorAttributeName, nil]; + return [[attributes retain] autorelease]; +} + ++ (NSAttributedString *)parseColorCodes:(NSString *)ansiString +{ + NSMutableAttributedString *retval = [[[NSMutableAttributedString alloc] + initWithString:@""] autorelease]; + NSUInteger length = [ansiString length]; + NSUInteger last = 0; + NSUInteger current = -1; + + static const int DefaultFGColor = 7; + static const int DefaultBGColor = 0; + bool bold = false; + bool blink = false; + bool reverse = false; + bool isReset = true; + int bgColor = DefaultBGColor; + int fgColor = DefaultFGColor; + + while((current = findNext(ansiString, @"\e[", current + 1)) != NSNotFound) + { + int updateLength = current - last + 1; + NSAttributedString *update = [[[NSAttributedString alloc] + initWithString:[ansiString substringWithRange:NSMakeRange(last, updateLength)] + attributes:attributes(bold, blink, reverse, bgColor, fgColor, isReset)] + autorelease]; + [retval appendAttributedString:update]; + last = current; + + NSUInteger end = findNext(ansiString, @"m", current + 1); + if(end == NSNotFound) + { + current = length; + } + else + { + bool valid = true; + int i; + for(i = current + 2; valid && (i < end); ++i) + { + unichar c = [ansiString characterAtIndex:i]; + switch(c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case ';': + break; + default: + valid = false; + } + } + if(valid) + { + NSArray *codes = [[ansiString substringWithRange: + NSMakeRange(current + 2, end - current - 2)] + componentsSeparatedByString:@";"]; + for(i = 0; i < [codes count]; ++i) + { + int c = [[codes objectAtIndex:i] intValue]; + switch(c) + { + case 0: + bold = false; + blink = false; + reverse = false; + isReset = true; + bgColor = DefaultBGColor; + fgColor = DefaultFGColor; + break; + case 1: + bold = true; + isReset = false; + break; + case 5: + blink = true; + isReset = false; + break; + case 7: + reverse = true; + isReset = false; + break; + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + fgColor = c - 30; + isReset = false; + break; + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + bgColor = c - 40; + isReset = false; + break; + } + } + + int codeLength = end - current + 1; + current += codeLength - 1; + last = current + 1; + } + } + } + if(last < length) + { + NSAttributedString *rest = [[[NSAttributedString alloc] + initWithString:[ansiString substringFromIndex:last] + attributes:attributes(bold, blink, reverse, bgColor, fgColor, isReset)] + autorelease]; + [retval appendAttributedString:rest]; + } + + return retval; +} + +@end diff --git a/src/MAC/ConsoleController.h b/src/MAC/ConsoleController.h new file mode 100644 index 0000000..6fadd72 --- /dev/null +++ b/src/MAC/ConsoleController.h @@ -0,0 +1,22 @@ +// This file is hereby placed in the Public Domain -- Neil Stevens + +#import + +@interface ConsoleController : NSWindowController +{ + IBOutlet NSTextView *textView; + + NSMutableString *log; + id launchDelegate; +} + +- (id)initWithWindow:(id)window; +- (void)awakeFromNib; +- (void)dealloc; + +- (void)launch:(NSString *)launchPath args:(NSArray *)args delegate:(id)delegate; + +- (void)taskComplete:(NSNotification *)notification; +- (void)dataReady:(NSNotification *)notification; + +@end diff --git a/src/MAC/ConsoleController.m b/src/MAC/ConsoleController.m new file mode 100644 index 0000000..bb9f4ef --- /dev/null +++ b/src/MAC/ConsoleController.m @@ -0,0 +1,98 @@ +// This file is hereby placed in the Public Domain -- Neil Stevens + +#import "ANSIString.h" +#import "ConsoleController.h" +#import "LauncherApp.h" + +@implementation ConsoleController + +- (id)initWithWindow:(id)window +{ + return [super initWithWindow:window]; +} + +- (void)awakeFromNib +{ + launchDelegate = nil; + log = [[[NSMutableString alloc] init] retain]; +} + +- (void)dealloc +{ + [log release]; + [super dealloc]; +} + +- (void)launch:(NSString *)path args:(NSArray *)args delegate:(id)delegate +{ + launchDelegate = delegate; + + // clear console + [log setString:@""]; + [textView setString:@""]; + + NSTask *task = [[NSTask alloc] init]; + [task retain]; + [task setLaunchPath:path]; + [task setArguments:args]; + NSPipe *standardOutput = [[NSPipe alloc] init]; + [standardOutput retain]; + [task setStandardOutput:standardOutput]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(dataReady:) + name:NSFileHandleReadCompletionNotification + object:[standardOutput fileHandleForReading]]; + + [[NSNotificationCenter defaultCenter] + addObserver:self selector:@selector(taskComplete:) + name:NSTaskDidTerminateNotification object:nil]; + + [task launch]; + [[standardOutput fileHandleForReading] readInBackgroundAndNotify]; +} + +- (void)dataReady:(NSNotification *)notification +{ + NSData *data = [[notification userInfo] + objectForKey:NSFileHandleNotificationDataItem]; + NSFileHandle *handle = [notification object]; + if([data length]) + { + NSString *string = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + [log appendString:string]; + [string release]; + + [[textView textStorage] beginEditing]; + [[textView textStorage] setAttributedString:[ANSIString parseColorCodes:log]]; + [[textView textStorage] endEditing]; + + // Scroll to bottom + [textView scrollRangeToVisible:NSMakeRange([[textView string] length], 0)]; + + [handle readInBackgroundAndNotify]; + } + else + { + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:NSFileHandleReadCompletionNotification + object:[notification object]]; + } +} + +- (void)taskComplete:(NSNotification *)notification +{ + NSTask *task = [notification object]; + if ([task terminationStatus] != 0) + [self showWindow:nil]; + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:NSTaskDidTerminateNotification + object:[notification object]]; + if(launchDelegate) + [launchDelegate taskEnded:self]; +} + +@end diff --git a/src/MAC/DrawerButton.h b/src/MAC/DrawerButton.h new file mode 100644 index 0000000..9e6f540 --- /dev/null +++ b/src/MAC/DrawerButton.h @@ -0,0 +1,14 @@ +// This file is hereby placed in the Public Domain -- Neil Stevens + +#import + +@interface DrawerButton : NSButton +{ + IBOutlet id drawer; +} + +- (void)drawerDidClose:(NSNotification *)notification; +- (void)drawerDidOpen:(NSNotification *)notification; +- (void)updateTitle; + +@end diff --git a/src/MAC/DrawerButton.m b/src/MAC/DrawerButton.m new file mode 100644 index 0000000..8abd4af --- /dev/null +++ b/src/MAC/DrawerButton.m @@ -0,0 +1,29 @@ +// This file is hereby placed in the Public Domain -- Neil Stevens + +#import "DrawerButton.h" + +@implementation DrawerButton + +- (void)drawerDidClose:(NSNotification *)notification +{ + [self updateTitle]; +} + +- (void)drawerDidOpen:(NSNotification *)notification +{ + [self updateTitle]; +} + +- (void)updateTitle +{ + int state = [drawer state]; + bool opening = state == NSDrawerOpenState | state == NSDrawerOpeningState; + NSString *newText = opening ? @"Hide " : @"Show "; + if([[self title] hasPrefix:@"Hide "] || [[self title] hasPrefix:@"Show "]) + { + [self setTitle:[newText stringByAppendingString: + [[self title] substringFromIndex:5]]]; + } +} + +@end diff --git a/src/MAC/English.lproj/MainMenu.nib/classes.nib b/src/MAC/English.lproj/MainMenu.nib/classes.nib new file mode 100644 index 0000000..7d2d8fd --- /dev/null +++ b/src/MAC/English.lproj/MainMenu.nib/classes.nib @@ -0,0 +1,80 @@ +{ + IBClasses = ( + { + CLASS = ConsoleController; + LANGUAGE = ObjC; + OUTLETS = {textView = NSTextView; }; + SUPERCLASS = NSWindowController; + }, + { + CLASS = DrawerButton; + LANGUAGE = ObjC; + OUTLETS = {drawer = NSDrawer; }; + SUPERCLASS = NSButton; + }, + { + ACTIONS = {buttonClicked = id; }; + CLASS = FileButtonController; + LANGUAGE = ObjC; + OUTLETS = {button = NSButton; field = id; }; + SUPERCLASS = NSObject; + }, + {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, + { + ACTIONS = { + demoButtonClicked = id; + disableSoundClicked = id; + gameButtonClicked = id; + openWebsite = id; + showConsoleClicked = id; + showGameFolderClicked = id; + startClicked = id; + }; + CLASS = LauncherApp; + LANGUAGE = ObjC; + OUTLETS = { + compatibilityLevelButton = id; + configFileButtonController = id; + consoleController = id; + debugDrawer = id; + demoDrawer = id; + demoFileButtonController = id; + demoMatrix = id; + disableGraphicsButton = id; + disableJoystickButton = id; + disableMouseButton = id; + disableMusicButton = id; + disableSoundButton = id; + disableSoundEffectsButton = id; + fastDemoButton = id; + fastMonstersButton = id; + ffToLevelField = id; + fullscreenButton = id; + gameButton = id; + gameMenu = id; + graphicsModeComboBox = id; + launchButton = id; + noDemoButton = id; + noMonstersButton = id; + playDemoButton = id; + resolutionComboBox = id; + respawnMonstersButton = id; + timeDemoButton = id; + wadDrawer = id; + wadViewController = id; + window = id; + }; + SUPERCLASS = NSObject; + }, + {CLASS = RMUDAnsiTextView; LANGUAGE = ObjC; SUPERCLASS = NSTextView; }, + {CLASS = ResolutionDataSource; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, + { + ACTIONS = {add = id; remove = id; }; + CLASS = WadViewController; + LANGUAGE = ObjC; + OUTLETS = {removeButton = id; view = id; }; + SUPERCLASS = NSObject; + } + ); + IBVersion = 1; +} \ No newline at end of file diff --git a/src/MAC/English.lproj/MainMenu.nib/info.nib b/src/MAC/English.lproj/MainMenu.nib/info.nib new file mode 100644 index 0000000..b2da8a7 --- /dev/null +++ b/src/MAC/English.lproj/MainMenu.nib/info.nib @@ -0,0 +1,78 @@ + + + + + IBDocumentLocation + 554 67 356 451 0 0 1280 1002 + IBEditorPositions + + 225 + 129 132 202 144 0 0 1280 1002 + 235 + 360 72 171 353 0 0 1280 1002 + 29 + 3 293 333 44 0 0 1280 1002 + + IBFramework Version + 446.1 + IBLockedObjects + + 324 + 318 + 319 + 325 + 614 + 615 + 616 + 617 + 618 + 619 + 622 + 623 + 624 + 625 + 613 + 626 + 647 + 631 + 637 + 642 + 643 + 644 + 645 + 646 + 650 + 659 + 660 + 661 + 590 + 316 + 358 + 365 + 460 + 461 + 462 + 572 + 610 + 665 + 666 + 752 + 753 + 757 + 758 + + IBLockedTabItems + + 361 + + IBOpenObjects + + 21 + 601 + + IBSystem Version + 8J135 + IBUsesTextArchiving + + + diff --git a/src/MAC/English.lproj/MainMenu.nib/keyedobjects.nib b/src/MAC/English.lproj/MainMenu.nib/keyedobjects.nib new file mode 100644 index 0000000..2d645cc --- /dev/null +++ b/src/MAC/English.lproj/MainMenu.nib/keyedobjects.nib @@ -0,0 +1,17352 @@ + + + + + $archiver + NSKeyedArchiver + $objects + + $null + + $class + + CF$UID + 1096 + + NSAccessibilityConnectors + + CF$UID + 1093 + + NSAccessibilityOidsKeys + + CF$UID + 1094 + + NSAccessibilityOidsValues + + CF$UID + 1095 + + NSClassesKeys + + CF$UID + 850 + + NSClassesValues + + CF$UID + 851 + + NSConnections + + CF$UID + 211 + + NSFontManager + + CF$UID + 0 + + NSFramework + + CF$UID + 5 + + NSNamesKeys + + CF$UID + 788 + + NSNamesValues + + CF$UID + 789 + + NSNextOid + 760 + NSObjectsKeys + + CF$UID + 776 + + NSObjectsValues + + CF$UID + 787 + + NSOidsKeys + + CF$UID + 852 + + NSOidsValues + + CF$UID + 853 + + NSRoot + + CF$UID + 2 + + NSVisibleWindows + + CF$UID + 7 + + + + $class + + CF$UID + 4 + + NSClassName + + CF$UID + 3 + + + NSApplication + + $classes + + NSCustomObject + NSObject + + $classname + NSCustomObject + + + $class + + CF$UID + 6 + + NS.string + IBCocoaFramework + + + $classes + + NSMutableString + NSString + NSObject + + $classname + NSMutableString + + + $class + + CF$UID + 210 + + NS.objects + + + CF$UID + 8 + + + + + $class + + CF$UID + 209 + + NSMaxSize + + CF$UID + 208 + + NSMinSize + + CF$UID + 207 + + NSScreenRect + + CF$UID + 206 + + NSViewClass + + CF$UID + 12 + + NSWTFlags + 1886914560 + NSWindowBacking + 2 + NSWindowClass + + CF$UID + 11 + + NSWindowRect + + CF$UID + 9 + + NSWindowStyleMask + 7 + NSWindowTitle + + CF$UID + 10 + + NSWindowView + + CF$UID + 13 + + + {{415, 638}, {410, 342}} + PrBoom Plus Launcher + NSWindow + + $class + + CF$UID + 6 + + NS.string + View + + + $class + + CF$UID + 83 + + NSFrame + + CF$UID + 205 + + NSNextResponder + + CF$UID + 0 + + NSSubviews + + CF$UID + 14 + + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 15 + + + CF$UID + 94 + + + CF$UID + 200 + + + + + $class + + CF$UID + 93 + + NSBorderType + 3 + NSBoxType + 0 + NSContentView + + CF$UID + 17 + + NSFrame + + CF$UID + 84 + + NSNextResponder + + CF$UID + 13 + + NSOffsets + + CF$UID + 85 + + NSSubviews + + CF$UID + 16 + + NSSuperview + + CF$UID + 13 + + NSTitleCell + + CF$UID + 86 + + NSTitlePosition + 2 + NSTransparent + + NSvFlags + 10 + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 17 + + + + + $class + + CF$UID + 83 + + NSFrame + + CF$UID + 82 + + NSNextResponder + + CF$UID + 15 + + NSSubviews + + CF$UID + 18 + + NSSuperview + + CF$UID + 15 + + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 19 + + + CF$UID + 36 + + + CF$UID + 65 + + + CF$UID + 69 + + + + + $class + + CF$UID + 35 + + NSCell + + CF$UID + 21 + + NSEnabled + + NSFrame + + CF$UID + 20 + + NSNextResponder + + CF$UID + 17 + + NSSuperview + + CF$UID + 17 + + NSvFlags + 260 + + {{97, 43}, {45, 17}} + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 26 + + NSCellFlags + 67239424 + NSCellFlags2 + 272629760 + NSContents + + CF$UID + 22 + + NSControlView + + CF$UID + 19 + + NSSupport + + CF$UID + 23 + + NSTextColor + + CF$UID + 31 + + + Game: + + $class + + CF$UID + 25 + + NSName + + CF$UID + 24 + + NSSize + 13 + NSfFlags + 1044 + + LucidaGrande + + $classes + + NSFont + NSObject + + $classname + NSFont + + + $class + + CF$UID + 30 + + NSCatalogName + + CF$UID + 27 + + NSColor + + CF$UID + 29 + + NSColorName + + CF$UID + 28 + + NSColorSpace + 6 + + System + controlColor + + $class + + CF$UID + 30 + + NSColorSpace + 3 + NSWhite + + MC42NjY2NjY2OQA= + + + + $classes + + NSColor + NSObject + + $classname + NSColor + + + $class + + CF$UID + 30 + + NSCatalogName + + CF$UID + 27 + + NSColor + + CF$UID + 33 + + NSColorName + + CF$UID + 32 + + NSColorSpace + 6 + + controlTextColor + + $class + + CF$UID + 30 + + NSColorSpace + 3 + NSWhite + + MAA= + + + + $classes + + NSTextFieldCell + NSActionCell + NSCell + NSObject + + $classname + NSTextFieldCell + + + $classes + + NSTextField + %NSTextField + NSControl + NSView + NSResponder + NSObject + + $classname + NSTextField + + + $class + + CF$UID + 64 + + NSCell + + CF$UID + 38 + + NSEnabled + + NSFrame + + CF$UID + 37 + + NSNextResponder + + CF$UID + 17 + + NSSuperview + + CF$UID + 17 + + NSvFlags + 290 + + {{144, 37}, {215, 26}} + + $class + + CF$UID + 63 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 39 + + NSAltersState + + NSArrowPosition + 1 + NSButtonFlags + 109199615 + NSButtonFlags2 + 1 + NSCellFlags + -2076049856 + NSCellFlags2 + 134219776 + NSControlView + + CF$UID + 36 + + NSKeyEquivalent + + CF$UID + 41 + + NSMenu + + CF$UID + 43 + + NSMenuItem + + CF$UID + 42 + + NSPeriodicDelay + 400 + NSPeriodicInterval + 75 + NSPreferredEdge + 3 + NSSupport + + CF$UID + 23 + + NSUsesItemFromMenu + + + + $class + + CF$UID + 25 + + NSName + + CF$UID + 24 + + NSSize + 13 + NSfFlags + 16 + + + + $class + + CF$UID + 6 + + NS.string + + + + $class + + CF$UID + 52 + + NSAction + + CF$UID + 51 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 43 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSState + 1 + NSTarget + + CF$UID + 38 + + NSTitle + + CF$UID + 44 + + + + $class + + CF$UID + 62 + + NSMenuItems + + CF$UID + 54 + + NSTitle + + CF$UID + 53 + + + Item1 + + $class + + CF$UID + 48 + + NSClassName + + CF$UID + 46 + + NSResourceName + + CF$UID + 47 + + + NSImage + NSMenuCheckmark + + $classes + + NSCustomResource + %NSCustomResource + NSObject + + $classname + NSCustomResource + + + $class + + CF$UID + 48 + + NSClassName + + CF$UID + 46 + + NSResourceName + + CF$UID + 50 + + + NSMenuMixedState + _popUpItemAction: + + $classes + + NSMenuItem + NSObject + + $classname + NSMenuItem + + + $class + + CF$UID + 6 + + NS.string + OtherViews + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 42 + + + CF$UID + 55 + + + CF$UID + 58 + + + + + $class + + CF$UID + 52 + + NSAction + + CF$UID + 57 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 43 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTarget + + CF$UID + 38 + + NSTitle + + CF$UID + 56 + + + Item2 + _popUpItemAction: + + $class + + CF$UID + 52 + + NSAction + + CF$UID + 60 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 43 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTarget + + CF$UID + 38 + + NSTitle + + CF$UID + 59 + + + Item3 + _popUpItemAction: + + $classes + + NSMutableArray + NSArray + NSObject + + $classname + NSMutableArray + + + $classes + + NSMenu + NSObject + + $classname + NSMenu + + + $classes + + NSPopUpButtonCell + NSMenuItemCell + NSButtonCell + %NSButtonCell + NSActionCell + NSCell + NSObject + + $classname + NSPopUpButtonCell + + + $classes + + NSPopUpButton + NSButton + NSControl + NSView + NSResponder + NSObject + + $classname + NSPopUpButton + + + $class + + CF$UID + 35 + + NSCell + + CF$UID + 67 + + NSEnabled + + NSFrame + + CF$UID + 66 + + NSNextResponder + + CF$UID + 17 + + NSSuperview + + CF$UID + 17 + + NSvFlags + 260 + + {{13, 13}, {129, 17}} + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 26 + + NSCellFlags + 67239424 + NSCellFlags2 + 272629760 + NSContents + + CF$UID + 68 + + NSControlView + + CF$UID + 65 + + NSSupport + + CF$UID + 23 + + NSTextColor + + CF$UID + 31 + + + Compatibility level: + + $class + + CF$UID + 64 + + NSCell + + CF$UID + 71 + + NSEnabled + + NSFrame + + CF$UID + 70 + + NSNextResponder + + CF$UID + 17 + + NSSuperview + + CF$UID + 17 + + NSvFlags + 290 + + {{144, 7}, {215, 26}} + + $class + + CF$UID + 63 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 39 + + NSAltersState + + NSArrowPosition + 1 + NSButtonFlags + 109199615 + NSButtonFlags2 + 1 + NSCellFlags + -2076049856 + NSCellFlags2 + 134219776 + NSControlView + + CF$UID + 69 + + NSKeyEquivalent + + CF$UID + 72 + + NSMenu + + CF$UID + 74 + + NSMenuItem + + CF$UID + 73 + + NSPeriodicDelay + 400 + NSPeriodicInterval + 75 + NSPreferredEdge + 3 + NSSupport + + CF$UID + 23 + + NSUsesItemFromMenu + + + + $class + + CF$UID + 6 + + NS.string + + + + $class + + CF$UID + 52 + + NSAction + + CF$UID + 75 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 74 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSState + 1 + NSTarget + + CF$UID + 71 + + NSTitle + + CF$UID + 44 + + + + $class + + CF$UID + 62 + + NSMenuItems + + CF$UID + 77 + + NSTitle + + CF$UID + 76 + + + _popUpItemAction: + + $class + + CF$UID + 6 + + NS.string + OtherViews + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 73 + + + CF$UID + 78 + + + CF$UID + 80 + + + + + $class + + CF$UID + 52 + + NSAction + + CF$UID + 79 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 74 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTarget + + CF$UID + 71 + + NSTitle + + CF$UID + 56 + + + _popUpItemAction: + + $class + + CF$UID + 52 + + NSAction + + CF$UID + 81 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 74 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTarget + + CF$UID + 71 + + NSTitle + + CF$UID + 59 + + + _popUpItemAction: + {{2, 2}, {372, 72}} + + $classes + + NSView + NSResponder + NSObject + + $classname + NSView + + {{17, 252}, {376, 89}} + {0, 0} + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 89 + + NSCellFlags + 67239424 + NSCellFlags2 + 0 + NSContents + + CF$UID + 87 + + NSSupport + + CF$UID + 88 + + NSTextColor + + CF$UID + 92 + + + Game + + $class + + CF$UID + 25 + + NSName + + CF$UID + 24 + + NSSize + 11 + NSfFlags + 3100 + + + $class + + CF$UID + 30 + + NSCatalogName + + CF$UID + 27 + + NSColor + + CF$UID + 91 + + NSColorName + + CF$UID + 90 + + NSColorSpace + 6 + + textBackgroundColor + + $class + + CF$UID + 30 + + NSColorSpace + 3 + NSWhite + + MQA= + + + + $class + + CF$UID + 30 + + NSColorSpace + 3 + NSWhite + + MCAwLjgwMDAwMDAxAA== + + + + $classes + + NSBox + NSView + NSResponder + NSObject + + $classname + NSBox + + + $class + + CF$UID + 199 + + NSAllowTruncatedLabels + + NSDrawsBackground + + NSFont + + CF$UID + 23 + + NSFrame + + CF$UID + 193 + + NSNextResponder + + CF$UID + 13 + + NSSelectedTabViewItem + + CF$UID + 195 + + NSSubviews + + CF$UID + 95 + + NSSuperview + + CF$UID + 13 + + NSTabViewItems + + CF$UID + 194 + + NSTvFlags + 4 + NSvFlags + 18 + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 96 + + + + + $class + + CF$UID + 83 + + NSFrame + + CF$UID + 192 + + NSNextKeyView + + CF$UID + 98 + + NSNextResponder + + CF$UID + 94 + + NSSubviews + + CF$UID + 97 + + NSSuperview + + CF$UID + 94 + + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 98 + + + CF$UID + 100 + + + CF$UID + 111 + + + CF$UID + 115 + + + CF$UID + 119 + + + CF$UID + 129 + + + CF$UID + 135 + + + CF$UID + 141 + + + CF$UID + 145 + + + CF$UID + 171 + + + CF$UID + 175 + + + + + $class + + CF$UID + 109 + + NSCell + + CF$UID + 101 + + NSEnabled + + NSFrame + + CF$UID + 99 + + NSNextKeyView + + CF$UID + 100 + + NSNextResponder + + CF$UID + 96 + + NSSuperview + + CF$UID + 96 + + NSvFlags + 264 + + {{18, 138}, {103, 18}} + + $class + + CF$UID + 109 + + NSCell + + CF$UID + 112 + + NSEnabled + + NSFrame + + CF$UID + 110 + + NSNextKeyView + + CF$UID + 111 + + NSNextResponder + + CF$UID + 96 + + NSSuperview + + CF$UID + 96 + + NSvFlags + 264 + + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 105 + + NSButtonFlags + 1211912703 + NSButtonFlags2 + 2 + NSCellFlags + 67239424 + NSCellFlags2 + 0 + NSContents + + CF$UID + 102 + + NSControlView + + CF$UID + 98 + + NSKeyEquivalent + + CF$UID + 40 + + NSNormalImage + + CF$UID + 103 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + No monsters + + $class + + CF$UID + 48 + + NSClassName + + CF$UID + 46 + + NSResourceName + + CF$UID + 104 + + + NSSwitch + + $class + + CF$UID + 107 + + NSImageName + + CF$UID + 106 + + + NSSwitch + + $classes + + NSButtonImageSource + NSObject + + $classname + NSButtonImageSource + + + $classes + + NSButtonCell + %NSButtonCell + NSActionCell + NSCell + NSObject + + $classname + NSButtonCell + + + $classes + + NSButton + NSControl + NSView + NSResponder + NSObject + + $classname + NSButton + + {{18, 118}, {111, 18}} + + $class + + CF$UID + 109 + + NSCell + + CF$UID + 116 + + NSEnabled + + NSFrame + + CF$UID + 114 + + NSNextKeyView + + CF$UID + 115 + + NSNextResponder + + CF$UID + 96 + + NSSuperview + + CF$UID + 96 + + NSvFlags + 264 + + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 105 + + NSButtonFlags + 1211912703 + NSButtonFlags2 + 2 + NSCellFlags + 67239424 + NSCellFlags2 + 0 + NSContents + + CF$UID + 113 + + NSControlView + + CF$UID + 100 + + NSKeyEquivalent + + CF$UID + 40 + + NSNormalImage + + CF$UID + 103 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + Fast monsters + {{18, 98}, {141, 18}} + + $class + + CF$UID + 109 + + NSCell + + CF$UID + 120 + + NSEnabled + + NSFrame + + CF$UID + 118 + + NSNextKeyView + + CF$UID + 119 + + NSNextResponder + + CF$UID + 96 + + NSSuperview + + CF$UID + 96 + + NSvFlags + 264 + + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 105 + + NSButtonFlags + 1211912703 + NSButtonFlags2 + 2 + NSCellFlags + 67239424 + NSCellFlags2 + 0 + NSContents + + CF$UID + 117 + + NSControlView + + CF$UID + 111 + + NSKeyEquivalent + + CF$UID + 40 + + NSNormalImage + + CF$UID + 103 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + Respawn monsters + {{18, 71}, {148, 18}} + + $class + + CF$UID + 128 + + NSCell + + CF$UID + 125 + + NSClassName + + CF$UID + 122 + + NSEnabled + + NSFrame + + CF$UID + 124 + + NSNextKeyView + + CF$UID + 94 + + NSNextResponder + + CF$UID + 96 + + NSOriginalClassName + + CF$UID + 123 + + NSSuperview + + CF$UID + 96 + + NSvFlags + 264 + + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 105 + + NSButtonFlags + 1211912703 + NSButtonFlags2 + 2 + NSCellFlags + -2080244224 + NSCellFlags2 + 0 + NSContents + + CF$UID + 121 + + NSControlView + + CF$UID + 115 + + NSKeyEquivalent + + CF$UID + 40 + + NSNormalImage + + CF$UID + 103 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + Full screen graphics + DrawerButton + NSButton + {{177, 62}, {173, 32}} + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 23 + + NSButtonFlags + -2038284033 + NSButtonFlags2 + 1 + NSCellFlags + 67239424 + NSCellFlags2 + 134217728 + NSContents + + CF$UID + 126 + + NSControlView + + CF$UID + 119 + + NSKeyEquivalent + + CF$UID + 127 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + Show Debug Options + + $class + + CF$UID + 6 + + NS.string + + + + $classes + + NSClassSwapper + NSObject + + $classname + NSClassSwapper + + + $class + + CF$UID + 128 + + NSCell + + CF$UID + 132 + + NSClassName + + CF$UID + 122 + + NSEnabled + + NSFrame + + CF$UID + 131 + + NSNextResponder + + CF$UID + 96 + + NSOriginalClassName + + CF$UID + 130 + + NSSuperview + + CF$UID + 96 + + NSvFlags + 264 + + NSButton + {{177, 126}, {173, 32}} + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 23 + + NSButtonFlags + -2038284033 + NSButtonFlags2 + 1 + NSCellFlags + 67239424 + NSCellFlags2 + 134217728 + NSContents + + CF$UID + 133 + + NSControlView + + CF$UID + 129 + + NSKeyEquivalent + + CF$UID + 134 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + Show Wad Options + + $class + + CF$UID + 6 + + NS.string + + + + $class + + CF$UID + 128 + + NSCell + + CF$UID + 138 + + NSClassName + + CF$UID + 122 + + NSEnabled + + NSFrame + + CF$UID + 137 + + NSNextResponder + + CF$UID + 96 + + NSOriginalClassName + + CF$UID + 136 + + NSSuperview + + CF$UID + 96 + + NSvFlags + 264 + + NSButton + {{177, 94}, {173, 32}} + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 23 + + NSButtonFlags + -2038284033 + NSButtonFlags2 + 1 + NSCellFlags + 67239424 + NSCellFlags2 + 134217728 + NSContents + + CF$UID + 139 + + NSControlView + + CF$UID + 135 + + NSKeyEquivalent + + CF$UID + 140 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + Show Demo Options + + $class + + CF$UID + 6 + + NS.string + + + + $class + + CF$UID + 35 + + NSCell + + CF$UID + 143 + + NSEnabled + + NSFrame + + CF$UID + 142 + + NSNextResponder + + CF$UID + 96 + + NSSuperview + + CF$UID + 96 + + NSvFlags + 256 + + {{17, 1}, {132, 17}} + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 26 + + NSCellFlags + 67239424 + NSCellFlags2 + 272629760 + NSContents + + CF$UID + 144 + + NSControlView + + CF$UID + 141 + + NSSupport + + CF$UID + 23 + + NSTextColor + + CF$UID + 31 + + + Graphics resolution: + + $class + + CF$UID + 170 + + NSCell + + CF$UID + 147 + + NSDataSource + + CF$UID + 0 + + NSEnabled + + NSFrame + + CF$UID + 146 + + NSNextResponder + + CF$UID + 96 + + NSSuperview + + CF$UID + 96 + + NSvFlags + 256 + + {{154, -3}, {193, 26}} + + $class + + CF$UID + 169 + + NSBackgroundColor + + CF$UID + 89 + + NSCellFlags + 74579521 + NSCellFlags2 + 272630784 + NSContents + + CF$UID + 148 + + NSControlView + + CF$UID + 145 + + NSDataSource + + CF$UID + 0 + + NSDelegate + + CF$UID + 145 + + NSDrawsBackground + + NSHasVerticalScroller + + NSSupport + + CF$UID + 23 + + NSTableView + + CF$UID + 149 + + NSTextColor + + CF$UID + 31 + + NSUsesDataSource + + NSVisibleItemCount + 5 + + + $class + + CF$UID + 6 + + NS.string + + + + $class + + CF$UID + 168 + + NSAction + + CF$UID + 167 + + NSBackgroundColor + + CF$UID + 161 + + NSColumnAutoresizingStyle + 1 + NSDelegate + + CF$UID + 147 + + NSDraggingSourceMaskForLocal + 15 + NSDraggingSourceMaskForNonLocal + 0 + NSEnabled + + NSFrameSize + + CF$UID + 151 + + NSGridColor + + CF$UID + 164 + + NSIntercellSpacingHeight + 2 + NSIntercellSpacingWidth + 3 + NSNextResponder + + CF$UID + 150 + + NSRowHeight + 16 + NSSuperview + + CF$UID + 150 + + NSTableColumns + + CF$UID + 153 + + NSTarget + + CF$UID + 147 + + NSTvFlags + -765427712 + NSWindow + + CF$UID + 152 + + NSvFlags + 274 + + $null + {13, 0} + $null + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 154 + + + + + $class + + CF$UID + 163 + + NSDataCell + + CF$UID + 160 + + NSHeaderCell + + CF$UID + 156 + + NSIdentifier + + CF$UID + 155 + + NSIsResizeable + + NSMaxWidth + 1000 + NSMinWidth + 10 + NSResizingMask + 3 + NSTableView + + CF$UID + 149 + + NSWidth + 10 + + 0 + + $class + + CF$UID + 159 + + NSBackgroundColor + + CF$UID + 158 + + NSCellFlags + 75628032 + NSCellFlags2 + 0 + NSContents + + CF$UID + 148 + + NSSupport + + CF$UID + 157 + + NSTextColor + + CF$UID + 91 + + + + $class + + CF$UID + 25 + + NSName + + CF$UID + 24 + + NSSize + 12 + NSfFlags + 16 + + + $class + + CF$UID + 30 + + NSColorSpace + 3 + NSWhite + + MC4zMzMzMzI5OQA= + + + + $classes + + NSTableHeaderCell + NSTextFieldCell + NSActionCell + NSCell + NSObject + + $classname + NSTableHeaderCell + + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 161 + + NSCellFlags + 338820672 + NSCellFlags2 + 1024 + NSControlView + + CF$UID + 149 + + NSDrawsBackground + + NSSupport + + CF$UID + 23 + + NSTextColor + + CF$UID + 31 + + + + $class + + CF$UID + 30 + + NSCatalogName + + CF$UID + 27 + + NSColor + + CF$UID + 29 + + NSColorName + + CF$UID + 162 + + NSColorSpace + 6 + + controlBackgroundColor + + $classes + + NSTableColumn + NSObject + + $classname + NSTableColumn + + + $class + + CF$UID + 30 + + NSCatalogName + + CF$UID + 27 + + NSColor + + CF$UID + 166 + + NSColorName + + CF$UID + 165 + + NSColorSpace + 6 + + gridColor + + $class + + CF$UID + 30 + + NSColorSpace + 3 + NSWhite + + MC41AA== + + + tableViewAction: + + $classes + + NSComboTableView + NSTableView + %NSTableView + NSControl + NSView + NSResponder + NSObject + + $classname + NSComboTableView + + + $classes + + NSComboBoxCell + NSTextFieldCell + NSActionCell + NSCell + NSObject + + $classname + NSComboBoxCell + + + $classes + + NSComboBox + NSTextField + %NSTextField + NSControl + NSView + NSResponder + NSObject + + $classname + NSComboBox + + + $class + + CF$UID + 35 + + NSCell + + CF$UID + 173 + + NSEnabled + + NSFrame + + CF$UID + 172 + + NSNextResponder + + CF$UID + 96 + + NSSuperview + + CF$UID + 96 + + NSvFlags + 256 + + {{17, 30}, {103, 17}} + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 26 + + NSCellFlags + 67239424 + NSCellFlags2 + 272629760 + NSContents + + CF$UID + 174 + + NSControlView + + CF$UID + 171 + + NSSupport + + CF$UID + 23 + + NSTextColor + + CF$UID + 31 + + + Graphics Mode: + + $class + + CF$UID + 170 + + NSCell + + CF$UID + 177 + + NSEnabled + + NSFrame + + CF$UID + 176 + + NSNextResponder + + CF$UID + 96 + + NSSuperview + + CF$UID + 96 + + NSvFlags + 256 + + {{125, 26}, {222, 26}} + + $class + + CF$UID + 169 + + NSBackgroundColor + + CF$UID + 89 + + NSCellFlags + 74579521 + NSCellFlags2 + 272630784 + NSContents + + CF$UID + 178 + + NSControlView + + CF$UID + 175 + + NSDelegate + + CF$UID + 175 + + NSDrawsBackground + + NSHasVerticalScroller + + NSPopUpListData + + CF$UID + 179 + + NSSupport + + CF$UID + 23 + + NSTableView + + CF$UID + 181 + + NSTextColor + + CF$UID + 31 + + NSVisibleItemCount + 2 + + 8 bit software + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 178 + + + CF$UID + 180 + + + + OpenGL + + $class + + CF$UID + 168 + + NSAction + + CF$UID + 191 + + NSBackgroundColor + + CF$UID + 161 + + NSColumnAutoresizingStyle + 1 + NSDataSource + + CF$UID + 177 + + NSDelegate + + CF$UID + 177 + + NSDraggingSourceMaskForLocal + 15 + NSDraggingSourceMaskForNonLocal + 0 + NSEnabled + + NSFrameSize + + CF$UID + 183 + + NSGridColor + + CF$UID + 164 + + NSIntercellSpacingHeight + 2 + NSIntercellSpacingWidth + 3 + NSNextResponder + + CF$UID + 182 + + NSRowHeight + 16 + NSSuperview + + CF$UID + 182 + + NSTableColumns + + CF$UID + 185 + + NSTarget + + CF$UID + 177 + + NSTvFlags + -765427712 + NSWindow + + CF$UID + 184 + + NSvFlags + 274 + + $null + {13, 36} + $null + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 186 + + + + + $class + + CF$UID + 163 + + NSDataCell + + CF$UID + 190 + + NSHeaderCell + + CF$UID + 187 + + NSIdentifier + + CF$UID + 155 + + NSIsResizeable + + NSMaxWidth + 1000 + NSMinWidth + 10 + NSResizingMask + 3 + NSTableView + + CF$UID + 181 + + NSWidth + 10 + + + $class + + CF$UID + 159 + + NSBackgroundColor + + CF$UID + 189 + + NSCellFlags + 75628032 + NSCellFlags2 + 0 + NSContents + + CF$UID + 188 + + NSSupport + + CF$UID + 157 + + NSTextColor + + CF$UID + 91 + + + + $class + + CF$UID + 6 + + NS.string + + + + $class + + CF$UID + 30 + + NSColorSpace + 3 + NSWhite + + MC4zMzMzMzI5OQA= + + + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 161 + + NSCellFlags + 338820672 + NSCellFlags2 + 1024 + NSControlView + + CF$UID + 181 + + NSDrawsBackground + + NSSupport + + CF$UID + 23 + + NSTextColor + + CF$UID + 31 + + + tableViewAction: + {{10, 7}, {358, 174}} + {{16, 56}, {378, 194}} + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 195 + + + + + $class + + CF$UID + 198 + + NSColor + + CF$UID + 26 + + NSIdentifier + + CF$UID + 196 + + NSLabel + + CF$UID + 197 + + NSTabView + + CF$UID + 94 + + NSView + + CF$UID + 96 + + + + $class + + CF$UID + 6 + + NS.string + 1 + + Options + + $classes + + NSTabViewItem + NSObject + + $classname + NSTabViewItem + + + $classes + + NSTabView + NSView + NSResponder + NSObject + + $classname + NSTabView + + + $class + + CF$UID + 109 + + NSCell + + CF$UID + 202 + + NSEnabled + + NSFrame + + CF$UID + 201 + + NSNextResponder + + CF$UID + 13 + + NSSuperview + + CF$UID + 13 + + NSvFlags + 257 + + {{311, 12}, {85, 32}} + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 23 + + NSButtonFlags + -2038284033 + NSButtonFlags2 + 1 + NSCellFlags + 67239424 + NSCellFlags2 + 134217728 + NSContents + + CF$UID + 203 + + NSControlView + + CF$UID + 200 + + NSKeyEquivalent + + CF$UID + 204 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + Launch + + {{1, 9}, {410, 342}} + {{0, 0}, {1280, 1002}} + {410, 307} + {411, 308} + + $classes + + NSWindowTemplate + NSObject + + $classname + NSWindowTemplate + + + $classes + + NSMutableSet + NSSet + NSObject + + $classname + NSMutableSet + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 212 + + + CF$UID + 219 + + + CF$UID + 223 + + + CF$UID + 229 + + + CF$UID + 233 + + + CF$UID + 238 + + + CF$UID + 242 + + + CF$UID + 246 + + + CF$UID + 252 + + + CF$UID + 257 + + + CF$UID + 262 + + + CF$UID + 267 + + + CF$UID + 272 + + + CF$UID + 277 + + + CF$UID + 281 + + + CF$UID + 285 + + + CF$UID + 303 + + + CF$UID + 339 + + + CF$UID + 343 + + + CF$UID + 345 + + + CF$UID + 347 + + + CF$UID + 349 + + + CF$UID + 351 + + + CF$UID + 353 + + + CF$UID + 354 + + + CF$UID + 356 + + + CF$UID + 357 + + + CF$UID + 358 + + + CF$UID + 420 + + + CF$UID + 422 + + + CF$UID + 424 + + + CF$UID + 426 + + + CF$UID + 428 + + + CF$UID + 430 + + + CF$UID + 432 + + + CF$UID + 434 + + + CF$UID + 436 + + + CF$UID + 438 + + + CF$UID + 498 + + + CF$UID + 505 + + + CF$UID + 558 + + + CF$UID + 625 + + + CF$UID + 681 + + + CF$UID + 682 + + + CF$UID + 683 + + + CF$UID + 685 + + + CF$UID + 687 + + + CF$UID + 689 + + + CF$UID + 691 + + + CF$UID + 693 + + + CF$UID + 695 + + + CF$UID + 697 + + + CF$UID + 699 + + + CF$UID + 701 + + + CF$UID + 703 + + + CF$UID + 705 + + + CF$UID + 707 + + + CF$UID + 709 + + + CF$UID + 711 + + + CF$UID + 713 + + + CF$UID + 715 + + + CF$UID + 717 + + + CF$UID + 721 + + + CF$UID + 722 + + + CF$UID + 724 + + + CF$UID + 726 + + + CF$UID + 728 + + + CF$UID + 730 + + + CF$UID + 732 + + + CF$UID + 734 + + + CF$UID + 735 + + + CF$UID + 736 + + + CF$UID + 737 + + + CF$UID + 738 + + + CF$UID + 739 + + + CF$UID + 741 + + + CF$UID + 742 + + + CF$UID + 743 + + + CF$UID + 747 + + + CF$UID + 748 + + + CF$UID + 750 + + + CF$UID + 751 + + + CF$UID + 753 + + + CF$UID + 757 + + + CF$UID + 760 + + + CF$UID + 762 + + + CF$UID + 764 + + + CF$UID + 765 + + + CF$UID + 766 + + + CF$UID + 768 + + + CF$UID + 769 + + + CF$UID + 771 + + + CF$UID + 774 + + + + + $class + + CF$UID + 218 + + NSLabel + + CF$UID + 217 + + NSSource + + CF$UID + 213 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 216 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 214 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 215 + + + + $class + + CF$UID + 62 + + NSMenuItems + + CF$UID + 409 + + NSName + + CF$UID + 411 + + NSTitle + + CF$UID + 407 + + + Minimize + m + + $class + + CF$UID + 6 + + NS.string + performMiniaturize: + + + $classes + + NSNibControlConnector + NSNibConnector + NSObject + + $classname + NSNibControlConnector + + + $class + + CF$UID + 218 + + NSLabel + + CF$UID + 222 + + NSSource + + CF$UID + 220 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 214 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 221 + + + Bring All to Front + + $class + + CF$UID + 6 + + NS.string + arrangeInFront: + + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 2 + + NSLabel + + CF$UID + 228 + + NSSource + + CF$UID + 224 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 227 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 225 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 226 + + + + $class + + CF$UID + 62 + + NSMenuItems + + CF$UID + 365 + + NSName + + CF$UID + 380 + + NSTitle + + CF$UID + 363 + + + Quit PrBoom Plus + q + + $class + + CF$UID + 6 + + NS.string + terminate: + + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 2 + + NSLabel + + CF$UID + 232 + + NSSource + + CF$UID + 230 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSMenu + + CF$UID + 225 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 231 + + + About PrBoom Plus + + $class + + CF$UID + 6 + + NS.string + orderFrontStandardAboutPanel: + + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 2 + + NSLabel + + CF$UID + 237 + + NSSource + + CF$UID + 234 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 236 + + NSKeyEquivModMask + 1572864 + NSMenu + + CF$UID + 225 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 235 + + + Hide Others + h + hideOtherApplications: + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 2 + + NSLabel + + CF$UID + 241 + + NSSource + + CF$UID + 239 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 236 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 225 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 240 + + + Hide PrBoom Plus + hide: + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 2 + + NSLabel + + CF$UID + 245 + + NSSource + + CF$UID + 243 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 225 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 244 + + + Show All + unhideAllApplications: + + $class + + CF$UID + 218 + + NSLabel + + CF$UID + 251 + + NSSource + + CF$UID + 247 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 250 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 248 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 249 + + + + $class + + CF$UID + 62 + + NSMenuItems + + CF$UID + 393 + + NSTitle + + CF$UID + 391 + + + Cut + x + + $class + + CF$UID + 6 + + NS.string + cut: + + + $class + + CF$UID + 218 + + NSLabel + + CF$UID + 256 + + NSSource + + CF$UID + 253 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 255 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 248 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 254 + + + Paste + v + + $class + + CF$UID + 6 + + NS.string + paste: + + + $class + + CF$UID + 218 + + NSLabel + + CF$UID + 261 + + NSSource + + CF$UID + 258 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 260 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 248 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 259 + + + Redo + Z + + $class + + CF$UID + 6 + + NS.string + redo: + + + $class + + CF$UID + 218 + + NSLabel + + CF$UID + 266 + + NSSource + + CF$UID + 263 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 265 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 248 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 264 + + + Select All + a + + $class + + CF$UID + 6 + + NS.string + selectAll: + + + $class + + CF$UID + 218 + + NSLabel + + CF$UID + 271 + + NSSource + + CF$UID + 268 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 270 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 248 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 269 + + + Undo + z + + $class + + CF$UID + 6 + + NS.string + undo: + + + $class + + CF$UID + 218 + + NSLabel + + CF$UID + 276 + + NSSource + + CF$UID + 273 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 275 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 248 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 274 + + + Copy + c + + $class + + CF$UID + 6 + + NS.string + copy: + + + $class + + CF$UID + 218 + + NSLabel + + CF$UID + 280 + + NSSource + + CF$UID + 278 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 248 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 279 + + + Delete + delete: + + $class + + CF$UID + 218 + + NSLabel + + CF$UID + 284 + + NSSource + + CF$UID + 282 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 214 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 283 + + + Zoom + performZoom: + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 286 + + NSLabel + + CF$UID + 301 + + NSSource + + CF$UID + 36 + + + + $class + + CF$UID + 62 + + NSMenuItems + + CF$UID + 288 + + NSName + + CF$UID + 40 + + NSNoAutoenable + + NSTitle + + CF$UID + 287 + + + Menu + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 289 + + + CF$UID + 291 + + + CF$UID + 293 + + + CF$UID + 295 + + + CF$UID + 297 + + + CF$UID + 299 + + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 286 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 290 + + + Doom + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 286 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 292 + + + Ultimate Doom + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 286 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 294 + + + Doom II + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 286 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 296 + + + TNT: Evilution + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 286 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 298 + + + The Plutonia Experiment + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 286 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 300 + + + Freedoom + menu + + $classes + + NSNibOutletConnector + NSNibConnector + NSObject + + $classname + NSNibOutletConnector + + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 304 + + NSLabel + + CF$UID + 301 + + NSSource + + CF$UID + 69 + + + + $class + + CF$UID + 62 + + NSMenuItems + + CF$UID + 305 + + NSName + + CF$UID + 40 + + NSNoAutoenable + + NSTitle + + CF$UID + 287 + + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 306 + + + CF$UID + 308 + + + CF$UID + 310 + + + CF$UID + 312 + + + CF$UID + 314 + + + CF$UID + 315 + + + CF$UID + 317 + + + CF$UID + 319 + + + CF$UID + 321 + + + CF$UID + 323 + + + CF$UID + 325 + + + CF$UID + 327 + + + CF$UID + 329 + + + CF$UID + 331 + + + CF$UID + 333 + + + CF$UID + 335 + + + CF$UID + 337 + + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 307 + + + Default + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 309 + + + Doom v1.2 + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 311 + + + Doom v1.666 + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 313 + + + Doom v1.9/Doom II + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 292 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 316 + + + Final Doom + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 318 + + + DosDoom + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 320 + + + TASDoom + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 322 + + + Boom Compatibility + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 324 + + + Boom v2.01 + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 326 + + + MBF + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 328 + + + PrBoom v2.03beta + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 330 + + + PrBoom v2.1.0 + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 332 + + + PrBoom v2.1.2 + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 334 + + + PrBoom v2.3.x + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 336 + + + PrBoom v2.4.0 + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 304 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 338 + + + PrBoom Current + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 340 + + NSLabel + + CF$UID + 342 + + NSSource + + CF$UID + 2 + + + + $class + + CF$UID + 4 + + NSClassName + + CF$UID + 341 + + + LauncherApp + delegate + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 36 + + NSLabel + + CF$UID + 344 + + NSSource + + CF$UID + 340 + + + gameButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 69 + + NSLabel + + CF$UID + 346 + + NSSource + + CF$UID + 340 + + + compatibilityLevelButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 98 + + NSLabel + + CF$UID + 348 + + NSSource + + CF$UID + 340 + + + noMonstersButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 100 + + NSLabel + + CF$UID + 350 + + NSSource + + CF$UID + 340 + + + fastMonstersButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 111 + + NSLabel + + CF$UID + 352 + + NSSource + + CF$UID + 340 + + + respawnMonstersButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 340 + + NSLabel + + CF$UID + 342 + + NSSource + + CF$UID + 8 + + + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 340 + + NSLabel + + CF$UID + 355 + + NSSource + + CF$UID + 200 + + + startClicked: + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 340 + + NSLabel + + CF$UID + 342 + + NSSource + + CF$UID + 286 + + + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 340 + + NSLabel + + CF$UID + 342 + + NSSource + + CF$UID + 304 + + + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 340 + + NSLabel + + CF$UID + 342 + + NSSource + + CF$UID + 359 + + + + $class + + CF$UID + 62 + + NSMenuItems + + CF$UID + 361 + + NSName + + CF$UID + 419 + + NSTitle + + CF$UID + 360 + + + MainMenu + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 362 + + + CF$UID + 381 + + + CF$UID + 390 + + + CF$UID + 395 + + + CF$UID + 406 + + + CF$UID + 412 + + + + + $class + + CF$UID + 52 + + NSAction + + CF$UID + 364 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 359 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSSubmenu + + CF$UID + 225 + + NSTitle + + CF$UID + 363 + + + PrBoom + submenuAction: + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 230 + + + CF$UID + 366 + + + CF$UID + 367 + + + CF$UID + 370 + + + CF$UID + 371 + + + CF$UID + 378 + + + CF$UID + 239 + + + CF$UID + 234 + + + CF$UID + 243 + + + CF$UID + 379 + + + CF$UID + 224 + + + + + $class + + CF$UID + 52 + + NSIsDisabled + + NSIsSeparator + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 225 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 40 + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 369 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 225 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 368 + + + Preferences… + , + + $class + + CF$UID + 52 + + NSIsDisabled + + NSIsSeparator + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 225 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 40 + + + + $class + + CF$UID + 52 + + NSAction + + CF$UID + 373 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 225 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSSubmenu + + CF$UID + 374 + + NSTitle + + CF$UID + 372 + + + Services + submenuAction: + + $class + + CF$UID + 62 + + NSMenuItems + + CF$UID + 376 + + NSName + + CF$UID + 377 + + NSTitle + + CF$UID + 375 + + + + $class + + CF$UID + 6 + + NS.string + Services + + + $class + + CF$UID + 61 + + NS.objects + + + _NSServicesMenu + + $class + + CF$UID + 52 + + NSIsDisabled + + NSIsSeparator + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 225 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 40 + + + + $class + + CF$UID + 52 + + NSIsDisabled + + NSIsSeparator + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 225 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 40 + + + _NSAppleMenu + + $class + + CF$UID + 52 + + NSAction + + CF$UID + 383 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 359 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSSubmenu + + CF$UID + 384 + + NSTitle + + CF$UID + 382 + + + File + submenuAction: + + $class + + CF$UID + 62 + + NSMenuItems + + CF$UID + 386 + + NSTitle + + CF$UID + 385 + + + + $class + + CF$UID + 6 + + NS.string + File + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 387 + + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 389 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 384 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 388 + + + Close + w + + $class + + CF$UID + 52 + + NSAction + + CF$UID + 392 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 359 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSSubmenu + + CF$UID + 248 + + NSTitle + + CF$UID + 391 + + + Edit + submenuAction: + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 268 + + + CF$UID + 258 + + + CF$UID + 394 + + + CF$UID + 247 + + + CF$UID + 273 + + + CF$UID + 253 + + + CF$UID + 278 + + + CF$UID + 263 + + + + + $class + + CF$UID + 52 + + NSIsDisabled + + NSIsSeparator + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 248 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 40 + + + + $class + + CF$UID + 52 + + NSAction + + CF$UID + 397 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 359 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSSubmenu + + CF$UID + 398 + + NSTitle + + CF$UID + 396 + + + Tools + submenuAction: + + $class + + CF$UID + 62 + + NSMenuItems + + CF$UID + 399 + + NSTitle + + CF$UID + 396 + + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 400 + + + CF$UID + 403 + + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 402 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 398 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 401 + + + Show Game Folder + o + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 405 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 398 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 404 + + + Show Console + C + + $class + + CF$UID + 52 + + NSAction + + CF$UID + 408 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 359 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSSubmenu + + CF$UID + 214 + + NSTitle + + CF$UID + 407 + + + Window + submenuAction: + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 213 + + + CF$UID + 282 + + + CF$UID + 410 + + + CF$UID + 220 + + + + + $class + + CF$UID + 52 + + NSIsDisabled + + NSIsSeparator + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 214 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 40 + + + _NSWindowsMenu + + $class + + CF$UID + 52 + + NSAction + + CF$UID + 414 + + NSKeyEquiv + + CF$UID + 40 + + NSKeyEquivModMask + 1048576 + NSMenu + + CF$UID + 359 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSSubmenu + + CF$UID + 415 + + NSTitle + + CF$UID + 413 + + + Help + submenuAction: + + $class + + CF$UID + 62 + + NSMenuItems + + CF$UID + 416 + + NSTitle + + CF$UID + 413 + + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 417 + + + + + $class + + CF$UID + 52 + + NSKeyEquiv + + CF$UID + 40 + + NSMenu + + CF$UID + 415 + + NSMixedImage + + CF$UID + 49 + + NSMnemonicLoc + 2147483647 + NSOnImage + + CF$UID + 45 + + NSTitle + + CF$UID + 418 + + + PrBoom Website + _NSMainMenu + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 8 + + NSLabel + + CF$UID + 421 + + NSSource + + CF$UID + 340 + + + window + + $class + + CF$UID + 218 + + NSLabel + + CF$UID + 423 + + NSSource + + CF$UID + 387 + + + performClose: + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 340 + + NSLabel + + CF$UID + 425 + + NSSource + + CF$UID + 417 + + + openWebsite: + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 115 + + NSLabel + + CF$UID + 427 + + NSSource + + CF$UID + 340 + + + fullscreenButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 200 + + NSLabel + + CF$UID + 429 + + NSSource + + CF$UID + 340 + + + launchButton + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 340 + + NSLabel + + CF$UID + 431 + + NSSource + + CF$UID + 36 + + + gameButtonClicked: + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 286 + + NSLabel + + CF$UID + 433 + + NSSource + + CF$UID + 340 + + + gameMenu + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 340 + + NSLabel + + CF$UID + 435 + + NSSource + + CF$UID + 403 + + + showConsoleClicked: + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 340 + + NSLabel + + CF$UID + 437 + + NSSource + + CF$UID + 400 + + + showGameFolderClicked: + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 441 + + NSLabel + + CF$UID + 421 + + NSSource + + CF$UID + 439 + + + + $class + + CF$UID + 440 + + NSNextResponder + + CF$UID + 0 + + + + $classes + + NSWindowController + NSResponder + NSObject + + $classname + NSWindowController + + + $class + + CF$UID + 209 + + NSMaxSize + + CF$UID + 497 + + NSMinSize + + CF$UID + 496 + + NSScreenRect + + CF$UID + 495 + + NSViewClass + + CF$UID + 445 + + NSWTFlags + 1618479104 + NSWindowBacking + 2 + NSWindowClass + + CF$UID + 444 + + NSWindowRect + + CF$UID + 442 + + NSWindowStyleMask + 14 + NSWindowTitle + + CF$UID + 443 + + NSWindowView + + CF$UID + 446 + + + {{3, 68}, {600, 450}} + PrBoom Console + + $class + + CF$UID + 6 + + NS.string + NSWindow + + + $class + + CF$UID + 6 + + NS.string + View + + + $class + + CF$UID + 83 + + NSFrame + + CF$UID + 494 + + NSNextResponder + + CF$UID + 0 + + NSSubviews + + CF$UID + 447 + + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 448 + + + + + $class + + CF$UID + 493 + + NSContentView + + CF$UID + 450 + + NSFrameSize + + CF$UID + 492 + + NSHScroller + + CF$UID + 489 + + NSNextKeyView + + CF$UID + 450 + + NSNextResponder + + CF$UID + 446 + + NSSubviews + + CF$UID + 449 + + NSSuperview + + CF$UID + 446 + + NSVScroller + + CF$UID + 485 + + NSsFlags + 16 + NSvFlags + 274 + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 450 + + + CF$UID + 485 + + + CF$UID + 489 + + + + + $class + + CF$UID + 484 + + NSBGColor + + CF$UID + 463 + + NSBounds + + CF$UID + 480 + + NSCursor + + CF$UID + 481 + + NSDocView + + CF$UID + 452 + + NSFrameSize + + CF$UID + 479 + + NSNextKeyView + + CF$UID + 452 + + NSNextResponder + + CF$UID + 448 + + NSSubviews + + CF$UID + 451 + + NSSuperview + + CF$UID + 448 + + NScvFlags + 4 + NSvFlags + 2304 + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 452 + + + + + $class + + CF$UID + 478 + + NSDelegate + + CF$UID + 0 + + NSFrame + + CF$UID + 453 + + NSMaxSize + + CF$UID + 477 + + NSNextResponder + + CF$UID + 450 + + NSSharedData + + CF$UID + 462 + + NSSuperview + + CF$UID + 450 + + NSTVFlags + 6 + NSTextContainer + + CF$UID + 454 + + NSvFlags + 2322 + + {{0, 100}, {585, 450}} + + $class + + CF$UID + 461 + + NSLayoutManager + + CF$UID + 455 + + NSTCFlags + 1 + NSTextView + + CF$UID + 452 + + NSWidth + 585 + + + $class + + CF$UID + 460 + + NSDelegate + + CF$UID + 0 + + NSLMFlags + 6 + NSTextContainers + + CF$UID + 459 + + NSTextStorage + + CF$UID + 456 + + + + $class + + CF$UID + 458 + + NSDelegate + + CF$UID + 0 + + NSString + + CF$UID + 457 + + + + $class + + CF$UID + 6 + + NS.string + + + + $classes + + NSTextStorage + NSMutableAttributedString + NSAttributedString + NSObject + + $classname + NSTextStorage + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 454 + + + + + $classes + + NSLayoutManager + NSObject + + $classname + NSLayoutManager + + + $classes + + NSTextContainer + NSObject + + $classname + NSTextContainer + + + $class + + CF$UID + 476 + + NSBackgroundColor + + CF$UID + 463 + + NSDefaultParagraphStyle + + CF$UID + 0 + + NSFlags + 11109 + NSInsertionColor + + CF$UID + 33 + + NSLinkAttributes + + CF$UID + 472 + + NSMarkedAttributes + + CF$UID + 0 + + NSSelectedAttributes + + CF$UID + 464 + + + + $class + + CF$UID + 30 + + NSColorSpace + 1 + NSRGB + + MSAxIDEA + + + + $class + + CF$UID + 471 + + NS.keys + + + CF$UID + 465 + + + CF$UID + 466 + + + NS.objects + + + CF$UID + 467 + + + CF$UID + 469 + + + + NSBackgroundColor + NSColor + + $class + + CF$UID + 30 + + NSCatalogName + + CF$UID + 27 + + NSColor + + CF$UID + 29 + + NSColorName + + CF$UID + 468 + + NSColorSpace + 6 + + selectedTextBackgroundColor + + $class + + CF$UID + 30 + + NSCatalogName + + CF$UID + 27 + + NSColor + + CF$UID + 33 + + NSColorName + + CF$UID + 470 + + NSColorSpace + 6 + + selectedTextColor + + $classes + + NSDictionary + NSObject + + $classname + NSDictionary + + + $class + + CF$UID + 471 + + NS.keys + + + CF$UID + 473 + + + CF$UID + 466 + + + NS.objects + + + CF$UID + 474 + + + CF$UID + 475 + + + + NSUnderline + 1 + + $class + + CF$UID + 30 + + NSColorSpace + 1 + NSRGB + + MCAwIDEA + + + + $classes + + NSTextViewSharedData + NSObject + + $classname + NSTextViewSharedData + + {627, 1e+07} + + $classes + + NSTextView + %NSTextView + NSText + NSView + NSResponder + NSObject + + $classname + NSTextView + + {585, 450} + {{0, 100}, {585, 450}} + + $class + + CF$UID + 483 + + NSCursorType + 1 + NSHotSpot + + CF$UID + 482 + + + {4, -5} + + $classes + + NSCursor + NSObject + + $classname + NSCursor + + + $classes + + NSClipView + NSView + NSResponder + NSObject + + $classname + NSClipView + + + $class + + CF$UID + 488 + + NSAction + + CF$UID + 487 + + NSCurValue + 1 + NSFrame + + CF$UID + 486 + + NSNextResponder + + CF$UID + 448 + + NSSuperview + + CF$UID + 448 + + NSTarget + + CF$UID + 448 + + NSvFlags + 256 + + {{585, 0}, {15, 435}} + _doScroller: + + $classes + + NSScroller + NSControl + NSView + NSResponder + NSObject + + $classname + NSScroller + + + $class + + CF$UID + 488 + + NSAction + + CF$UID + 491 + + NSCurValue + 1 + NSFrame + + CF$UID + 490 + + NSNextResponder + + CF$UID + 448 + + NSPercent + 0.94565218687057495 + NSSuperview + + CF$UID + 448 + + NSTarget + + CF$UID + 448 + + NSsFlags + 1 + NSvFlags + 256 + + {{-100, -100}, {87, 18}} + _doScroller: + {600, 450} + + $classes + + NSScrollView + NSView + NSResponder + NSObject + + $classname + NSScrollView + + {{1, 9}, {600, 450}} + {{0, 0}, {1280, 1002}} + {600, 472} + {3.40282e+38, 3.40282e+38} + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 8 + + NSLabel + + CF$UID + 504 + + NSSource + + CF$UID + 499 + + + + $class + + CF$UID + 503 + + NSContentSize + + CF$UID + 500 + + NSDelegate + + CF$UID + 0 + + NSLeadingOffset + 0.0 + NSMaxContentSize + + CF$UID + 502 + + NSMinContentSize + + CF$UID + 501 + + NSNextResponder + + CF$UID + 0 + + NSParentWindow + + CF$UID + 0 + + NSPreferredEdge + 1 + NSTrailingOffset + 15 + + {200, 253} + {364, 177} + {364, 177} + + $classes + + NSDrawer + NSResponder + NSObject + + $classname + NSDrawer + + parentWindow + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 506 + + NSLabel + + CF$UID + 557 + + NSSource + + CF$UID + 499 + + + + $class + + CF$UID + 556 + + NSClassName + + CF$UID + 554 + + NSExtension + + CF$UID + 555 + + NSFrame + + CF$UID + 553 + + NSNextResponder + + CF$UID + 507 + + NSSubviews + + CF$UID + 508 + + NSSuperview + + CF$UID + 507 + + + + $class + + CF$UID + 83 + + NSFrame + + CF$UID + 778 + + NSNextResponder + + CF$UID + 0 + + NSSubviews + + CF$UID + 777 + + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 509 + + + CF$UID + 513 + + + CF$UID + 517 + + + CF$UID + 519 + + + CF$UID + 523 + + + CF$UID + 527 + + + CF$UID + 533 + + + CF$UID + 535 + + + CF$UID + 539 + + + CF$UID + 547 + + + + + $class + + CF$UID + 109 + + NSCell + + CF$UID + 511 + + NSEnabled + + NSFrame + + CF$UID + 510 + + NSNextResponder + + CF$UID + 506 + + NSSuperview + + CF$UID + 506 + + NSvFlags + 264 + + {{18, 116}, {127, 18}} + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 105 + + NSButtonFlags + 1211912703 + NSButtonFlags2 + 2 + NSCellFlags + 67239424 + NSCellFlags2 + 0 + NSContents + + CF$UID + 512 + + NSControlView + + CF$UID + 509 + + NSKeyEquivalent + + CF$UID + 40 + + NSNormalImage + + CF$UID + 103 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + Disable graphics + + $class + + CF$UID + 109 + + NSCell + + CF$UID + 515 + + NSEnabled + + NSFrame + + CF$UID + 514 + + NSNextResponder + + CF$UID + 506 + + NSSuperview + + CF$UID + 506 + + NSvFlags + 264 + + {{18, 136}, {121, 18}} + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 105 + + NSButtonFlags + 1211912703 + NSButtonFlags2 + 2 + NSCellFlags + 67239424 + NSCellFlags2 + 0 + NSContents + + CF$UID + 516 + + NSControlView + + CF$UID + 513 + + NSKeyEquivalent + + CF$UID + 40 + + NSNormalImage + + CF$UID + 103 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + Disable joystick + + $class + + CF$UID + 109 + + NSCell + + CF$UID + 520 + + NSEnabled + + NSFrame + + CF$UID + 518 + + NSNextKeyView + + CF$UID + 519 + + NSNextResponder + + CF$UID + 506 + + NSSuperview + + CF$UID + 506 + + NSvFlags + 264 + + {{18, 96}, {115, 18}} + + $class + + CF$UID + 109 + + NSCell + + CF$UID + 524 + + NSEnabled + + NSFrame + + CF$UID + 522 + + NSNextKeyView + + CF$UID + 523 + + NSNextResponder + + CF$UID + 506 + + NSSuperview + + CF$UID + 506 + + NSvFlags + 264 + + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 105 + + NSButtonFlags + 1211912703 + NSButtonFlags2 + 2 + NSCellFlags + 67239424 + NSCellFlags2 + 0 + NSContents + + CF$UID + 521 + + NSControlView + + CF$UID + 517 + + NSKeyEquivalent + + CF$UID + 40 + + NSNormalImage + + CF$UID + 103 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + Disable mouse + {{18, 76}, {158, 18}} + + $class + + CF$UID + 109 + + NSCell + + CF$UID + 528 + + NSEnabled + + NSFrame + + CF$UID + 526 + + NSNextKeyView + + CF$UID + 527 + + NSNextResponder + + CF$UID + 506 + + NSSuperview + + CF$UID + 506 + + NSvFlags + 264 + + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 105 + + NSButtonFlags + 1211912703 + NSButtonFlags2 + 2 + NSCellFlags + 67239424 + NSCellFlags2 + 0 + NSContents + + CF$UID + 525 + + NSControlView + + CF$UID + 519 + + NSKeyEquivalent + + CF$UID + 40 + + NSNormalImage + + CF$UID + 103 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + Disable all sound + {{37, 56}, {158, 18}} + + $class + + CF$UID + 109 + + NSCell + + CF$UID + 531 + + NSEnabled + + NSFrame + + CF$UID + 530 + + NSNextResponder + + CF$UID + 506 + + NSSuperview + + CF$UID + 506 + + NSvFlags + 264 + + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 105 + + NSButtonFlags + 1211912703 + NSButtonFlags2 + 2 + NSCellFlags + 67239424 + NSCellFlags2 + 0 + NSContents + + CF$UID + 529 + + NSControlView + + CF$UID + 523 + + NSKeyEquivalent + + CF$UID + 40 + + NSNormalImage + + CF$UID + 103 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + Disable sound effects + {{37, 36}, {110, 18}} + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 105 + + NSButtonFlags + 1211912703 + NSButtonFlags2 + 2 + NSCellFlags + 67239424 + NSCellFlags2 + 0 + NSContents + + CF$UID + 532 + + NSControlView + + CF$UID + 527 + + NSKeyEquivalent + + CF$UID + 40 + + NSNormalImage + + CF$UID + 103 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + Disable music + + $class + + CF$UID + 35 + + NSCell + + CF$UID + 536 + + NSEnabled + + NSFrame + + CF$UID + 534 + + NSNextKeyView + + CF$UID + 535 + + NSNextResponder + + CF$UID + 506 + + NSSuperview + + CF$UID + 506 + + NSvFlags + 268 + + {{17, 10}, {70, 17}} + + $class + + CF$UID + 35 + + NSCell + + CF$UID + 540 + + NSEnabled + + NSFrame + + CF$UID + 538 + + NSNextKeyView + + CF$UID + 539 + + NSNextResponder + + CF$UID + 506 + + NSSuperview + + CF$UID + 506 + + NSvFlags + 266 + + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 26 + + NSCellFlags + 604110336 + NSCellFlags2 + 272629760 + NSContents + + CF$UID + 537 + + NSControlView + + CF$UID + 533 + + NSSupport + + CF$UID + 23 + + NSTextColor + + CF$UID + 31 + + + Config file + {{92, 8}, {170, 22}} + + $class + + CF$UID + 109 + + NSCell + + CF$UID + 544 + + NSEnabled + + NSFrame + + CF$UID + 543 + + NSNextResponder + + CF$UID + 506 + + NSSuperview + + CF$UID + 506 + + NSvFlags + 265 + + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 89 + + NSCellFlags + -1804468671 + NSCellFlags2 + 272630784 + NSContents + + CF$UID + 40 + + NSControlView + + CF$UID + 535 + + NSDrawsBackground + + NSSupport + + CF$UID + 23 + + NSTextColor + + CF$UID + 541 + + + + $class + + CF$UID + 30 + + NSCatalogName + + CF$UID + 27 + + NSColor + + CF$UID + 33 + + NSColorName + + CF$UID + 542 + + NSColorSpace + 6 + + textColor + {{264, 0}, {87, 32}} + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 23 + + NSButtonFlags + -2038284033 + NSButtonFlags2 + 1 + NSCellFlags + 67239424 + NSCellFlags2 + 134217728 + NSContents + + CF$UID + 545 + + NSControlView + + CF$UID + 539 + + NSKeyEquivalent + + CF$UID + 546 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + Choose + + $class + + CF$UID + 6 + + NS.string + + + + $class + + CF$UID + 35 + + NSCell + + CF$UID + 549 + + NSEnabled + + NSFrame + + CF$UID + 548 + + NSNextResponder + + CF$UID + 506 + + NSSuperview + + CF$UID + 506 + + NSvFlags + 264 + + {{17, 160}, {114, 17}} + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 26 + + NSCellFlags + 67239424 + NSCellFlags2 + 272629760 + NSContents + + CF$UID + 550 + + NSControlView + + CF$UID + 547 + + NSSupport + + CF$UID + 551 + + NSTextColor + + CF$UID + 31 + + + Debug Options + + $class + + CF$UID + 25 + + NSName + + CF$UID + 552 + + NSSize + 13 + NSfFlags + 2072 + + LucidaGrande-Bold + {{20, 328}, {364, 177}} + + $class + + CF$UID + 6 + + NS.string + NSView + + NSResponder + + $classes + + NSCustomView + NSView + NSResponder + NSObject + + $classname + NSCustomView + + contentView + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 563 + + NSLabel + + CF$UID + 557 + + NSSource + + CF$UID + 559 + + + + $class + + CF$UID + 503 + + NSContentSize + + CF$UID + 560 + + NSDelegate + + CF$UID + 0 + + NSLeadingOffset + 0.0 + NSMaxContentSize + + CF$UID + 562 + + NSMinContentSize + + CF$UID + 561 + + NSNextResponder + + CF$UID + 0 + + NSParentWindow + + CF$UID + 0 + + NSPreferredEdge + 2 + NSTrailingOffset + 15 + + {364, 253} + {364, 126} + {364, 126} + + $class + + CF$UID + 556 + + NSClassName + + CF$UID + 624 + + NSExtension + + CF$UID + 555 + + NSFrame + + CF$UID + 623 + + NSNextResponder + + CF$UID + 507 + + NSSubviews + + CF$UID + 564 + + NSSuperview + + CF$UID + 507 + + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 565 + + + CF$UID + 569 + + + CF$UID + 571 + + + CF$UID + 607 + + + CF$UID + 611 + + + CF$UID + 614 + + + CF$UID + 618 + + + + + $class + + CF$UID + 35 + + NSCell + + CF$UID + 567 + + NSEnabled + + NSFrame + + CF$UID + 566 + + NSNextResponder + + CF$UID + 563 + + NSSuperview + + CF$UID + 563 + + NSvFlags + 264 + + {{17, 109}, {114, 17}} + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 26 + + NSCellFlags + 67239424 + NSCellFlags2 + 272629760 + NSContents + + CF$UID + 568 + + NSControlView + + CF$UID + 565 + + NSSupport + + CF$UID + 551 + + NSTextColor + + CF$UID + 31 + + + Demo Options + + $class + + CF$UID + 605 + + NSBackgroundColor + + CF$UID + 26 + + NSCellBackgroundColor + + CF$UID + 91 + + NSCellClass + + CF$UID + 596 + + NSCellSize + + CF$UID + 594 + + NSCells + + CF$UID + 572 + + NSEnabled + + NSFont + + CF$UID + 23 + + NSFrame + + CF$UID + 570 + + NSIntercellSpacing + + CF$UID + 595 + + NSMatrixFlags + 1143472128 + NSNextKeyView + + CF$UID + 571 + + NSNextResponder + + CF$UID + 563 + + NSNumCols + 2 + NSNumRows + 2 + NSProtoCell + + CF$UID + 597 + + NSSelectedCell + + CF$UID + 573 + + NSSuperview + + CF$UID + 563 + + NSvFlags + 266 + + {{18, 60}, {328, 42}} + + $class + + CF$UID + 35 + + NSCell + + CF$UID + 608 + + NSEnabled + + NSFrame + + CF$UID + 606 + + NSNextKeyView + + CF$UID + 607 + + NSNextResponder + + CF$UID + 563 + + NSSuperview + + CF$UID + 563 + + NSvFlags + 268 + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 573 + + + CF$UID + 578 + + + CF$UID + 590 + + + CF$UID + 592 + + + + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 577 + + NSAlternateImage + + CF$UID + 575 + + NSButtonFlags + 1211912703 + NSButtonFlags2 + 0 + NSCellFlags + -2080244224 + NSCellFlags2 + 0 + NSContents + + CF$UID + 574 + + NSControlView + + CF$UID + 569 + + NSKeyEquivalent + + CF$UID + 577 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + No demo + + $class + + CF$UID + 107 + + NSImageName + + CF$UID + 576 + + + NSRadioButton + + $class + + CF$UID + 6 + + NS.string + + + + $class + + CF$UID + 108 + + NSAlternateImage + + CF$UID + 575 + + NSButtonFlags + 1211912703 + NSButtonFlags2 + 0 + NSCellFlags + 67239424 + NSCellFlags2 + 0 + NSContents + + CF$UID + 579 + + NSControlView + + CF$UID + 569 + + NSNormalImage + + CF$UID + 580 + + NSPeriodicDelay + 400 + NSPeriodicInterval + 75 + NSSupport + + CF$UID + 23 + + + Fast demo + + $class + + CF$UID + 589 + + NSColor + + CF$UID + 588 + + NSImageFlags + 549453824 + NSReps + + CF$UID + 582 + + NSSize + + CF$UID + 581 + + + {18, 18} + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 583 + + + + + $class + + CF$UID + 587 + + NS.objects + + + CF$UID + 155 + + + CF$UID + 584 + + + + + $class + + CF$UID + 586 + + NSTIFFRepresentation + + CF$UID + 585 + + + + TU0AKgAABRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMA + AAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAwAAADwRERGLJycnySsrK/A1NTXwIyMjyRwcHIsJCQk8AAAA + AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFRUVdVBQUOCoqKj/ + 29vb//n5+f/6+vr/2tra/6qqqv9UVFTgHx8fdQAAAAUAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAUZGRl5dXV198PDw//8/Pz///////////////////////// + ///U1NT/fHx89yUlJXkAAAAFAAAAAAAAAAAAAAAAAAAAAxEREUZqamrmtbW1 + /+3t7f/+/v7//v7+//7+/v/9/f3//f39//39/f/39/f/xMTE/3d3d+YZGRlG + AAAAAwAAAAAAAAAAAAAACkJCQqGtra3/xsbG/+vr6//y8vL/9fX1//X19f/z + 8/P/9fX1//Ly8v/u7u7/0tLS/6+vr/9KSkqhAAAACgAAAAAAAAAAAAAAF3h4 + eN2/v7//z8/P/93d3f/q6ur/7+/v/+/v7//w8PD/7e3t/+3t7f/i4uL/zs7O + /8XFxf98fHzdAAAAFwAAAAAAAAADAAAAJKSkpPjOzs7/2dnZ/+Dg4P/i4uL/ + 5eXl/+bm5v/n5+f/5eXl/+Li4v/e3t7/2tra/9DQ0P+srKz4AAAAJAAAAAMA + AAADAAAALrCwsPrW1tb/3t7e/+Tk5P/p6en/6+vr/+zs7P/p6en/6+vr/+fn + 5//k5OT/4ODg/9nZ2f+zs7P6AAAALgAAAAMAAAADAAAALp2dnezg4OD/5eXl + /+rq6v/u7u7/8PDw//Dw8P/x8fH/8PDw/+7u7v/q6ur/5ubm/+Hh4f+ZmZns + AAAALgAAAAMAAAADAAAAJG5ubs/l5eX/6enp/+/v7//y8vL/9vb2//r6+v/5 + +fn/9/f3//b29v/x8fH/6+vr/+Tk5P9ra2vPAAAAJAAAAAMAAAAAAAAAFy4u + LpPCwsL67Ozs//Pz8//5+fn//v7+//7+/v/+/v7//v7+//v7+//19fX/8PDw + /8LCwvosLCyTAAAAFwAAAAAAAAAAAAAACgAAAENfX1/S5OTk/vn5+f/+/v7/ + ///////////////////////////8/Pz/5ubm/l9fX9IAAABDAAAACgAAAAAA + AAAAAAAAAwAAABcAAABlYmJi3NLS0v3///////////////////////////// + ///V1dX9ZGRk3AAAAGUAAAAXAAAAAwAAAAAAAAAAAAAAAAAAAAUAAAAfAAAA + ZTMzM8KAgIDwv7+//O3t7f/t7e3/v7+//ICAgPAzMzPCAAAAZQAAAB8AAAAF + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwAAAEMAAAB3AAAAnwAAALMA + AACzAAAAnwAAAHcAAABDAAAAFwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAwAAAAoAAAAXAAAAJAAAAC4AAAAuAAAAJAAAABcAAAAKAAAA + AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + DgEAAAMAAAABABIAAAEBAAMAAAABABIAAAECAAMAAAAEAAAFxgEDAAMAAAAB + AAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMA + AAABAAQAAAEWAAMAAAABBxwAAAEXAAQAAAABAAAFEAEcAAMAAAABAAEAAAFS + AAMAAAABAAEAAAFTAAMAAAAEAAAFzodzAAcAAAM4AAAF1gAAAAAACAAIAAgA + CAABAAEAAQABAAADOGFwcGwCAAAAbW50clJHQiBYWVogB9UACgAQAAQABQAz + YWNzcEFQUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA0y1h + cHBspKaZrF1n3/yupoc/31HBfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAANclhZWgAAASAAAAAUZ1hZWgAAATQAAAAUYlhZWgAAAUgAAAAUd3Rw + dAAAAVwAAAAUY2hhZAAAAXAAAAAsclRSQwAAAZwAAAAOZ1RSQwAAAawAAAAO + YlRSQwAAAbwAAAAOdmNndAAAAcwAAAAwbmRpbgAAAfwAAAA4ZGVzYwAAAjQA + AACrY3BydAAAAuAAAAAtbW1vZAAAAxAAAAAoWFlaIAAAAAAAAG+NAAA9QgAA + AW5YWVogAAAAAAAAYOAAAKnfAAAazFhZWiAAAAAAAAAmaQAAGSEAALbkWFla + IAAAAAAAAPAYAAEAAAABF3tzZjMyAAAAAAABDwQAAAdY///yjgAACccAAPvu + ///7Vf///aoAAAO4AADACWN1cnYAAAAAAAAAAQHNAABjdXJ2AAAAAAAAAAEB + zQAAY3VydgAAAAAAAAABAc0AAHZjZ3QAAAAAAAAAAQAA5zYAAAAAAAEAAAAA + 5zYAAAAAAAEAAAAA5zYAAAAAAAEAAG5kaW4AAAAAAAAAMAAAokAAAFqAAABN + wAAAlMAAACYXAAAW2wAAT0AAAFSAAAH+NQAB/jUAAf41ZGVzYwAAAAAAAAAb + U3luY01hc3RlciA5MTJOIENhbGlicmF0ZWQAAAAAAAAAABsAUwB5AG4AYwBN + AGEAcwB0AGUAcgAgADkAMQAyAE4AIABDAGEAbABpAGIAcgBhAHQAZQBkAAAA + ABtTeW5jTWFzdGVyIDkxMk4gQ2FsaWJyYXRlZAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRleHQAAAAAQ29weXJpZ2h0IEFw + cGxlIENvbXB1dGVyLCBJbmMuLCAyMDAzAAAAAG1tb2QAAAAAAABMLQAAAR9N + SjE5vWVNgAAAAAAAAAAAAAAAAAAAAAA= + + + $classes + + NSBitmapImageRep + NSImageRep + NSObject + + $classname + NSBitmapImageRep + + + $classes + + NSArray + NSObject + + $classname + NSArray + + + $class + + CF$UID + 30 + + NSColorSpace + 3 + NSWhite + + MCAwAA== + + + + $classes + + NSImage + %NSImage + NSObject + + $classname + NSImage + + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 577 + + NSAlternateImage + + CF$UID + 575 + + NSButtonFlags + 1211912703 + NSButtonFlags2 + 0 + NSCellFlags + 67239424 + NSCellFlags2 + 0 + NSContents + + CF$UID + 591 + + NSControlView + + CF$UID + 569 + + NSKeyEquivalent + + CF$UID + 577 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + NSTag + 1 + + Play demo + + $class + + CF$UID + 108 + + NSAlternateImage + + CF$UID + 575 + + NSButtonFlags + 1211912703 + NSButtonFlags2 + 0 + NSCellFlags + 67239424 + NSCellFlags2 + 0 + NSContents + + CF$UID + 593 + + NSControlView + + CF$UID + 569 + + NSNormalImage + + CF$UID + 580 + + NSPeriodicDelay + 400 + NSPeriodicInterval + 75 + NSSupport + + CF$UID + 23 + + + Time demo + {162, 20} + {4, 2} + NSActionCell + + $class + + CF$UID + 108 + + NSAlternateImage + + CF$UID + 575 + + NSButtonFlags + 1211650559 + NSButtonFlags2 + 0 + NSCellFlags + 67239424 + NSCellFlags2 + 0 + NSContents + + CF$UID + 598 + + NSNormalImage + + CF$UID + 599 + + NSPeriodicDelay + 400 + NSPeriodicInterval + 75 + NSSupport + + CF$UID + 23 + + + Radio + + $class + + CF$UID + 589 + + NSColor + + CF$UID + 588 + + NSImageFlags + 549453824 + NSReps + + CF$UID + 601 + + NSSize + + CF$UID + 600 + + + {18, 18} + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 602 + + + + + $class + + CF$UID + 587 + + NS.objects + + + CF$UID + 155 + + + CF$UID + 603 + + + + + $class + + CF$UID + 586 + + NSTIFFRepresentation + + CF$UID + 604 + + + + TU0AKgAABRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMA + AAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAwAAADwRERGLJycnySsrK/A1NTXwIyMjyRwcHIsJCQk8AAAA + AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFRUVdVBQUOCoqKj/ + 29vb//n5+f/6+vr/2tra/6qqqv9UVFTgHx8fdQAAAAUAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAUZGRl5dXV198PDw//8/Pz///////////////////////// + ///U1NT/fHx89yUlJXkAAAAFAAAAAAAAAAAAAAAAAAAAAxEREUZqamrmtbW1 + /+3t7f/+/v7//v7+//7+/v/9/f3//f39//39/f/39/f/xMTE/3d3d+YZGRlG + AAAAAwAAAAAAAAAAAAAACkJCQqGtra3/xsbG/+vr6//y8vL/9fX1//X19f/z + 8/P/9fX1//Ly8v/u7u7/0tLS/6+vr/9KSkqhAAAACgAAAAAAAAAAAAAAF3h4 + eN2/v7//z8/P/93d3f/q6ur/7+/v/+/v7//w8PD/7e3t/+3t7f/i4uL/zs7O + /8XFxf98fHzdAAAAFwAAAAAAAAADAAAAJKSkpPjOzs7/2dnZ/+Dg4P/i4uL/ + 5eXl/+bm5v/n5+f/5eXl/+Li4v/e3t7/2tra/9DQ0P+srKz4AAAAJAAAAAMA + AAADAAAALrCwsPrW1tb/3t7e/+Tk5P/p6en/6+vr/+zs7P/p6en/6+vr/+fn + 5//k5OT/4ODg/9nZ2f+zs7P6AAAALgAAAAMAAAADAAAALp2dnezg4OD/5eXl + /+rq6v/u7u7/8PDw//Dw8P/x8fH/8PDw/+7u7v/q6ur/5ubm/+Hh4f+ZmZns + AAAALgAAAAMAAAADAAAAJG5ubs/l5eX/6enp/+/v7//y8vL/9vb2//r6+v/5 + +fn/9/f3//b29v/x8fH/6+vr/+Tk5P9ra2vPAAAAJAAAAAMAAAAAAAAAFy4u + LpPCwsL67Ozs//Pz8//5+fn//v7+//7+/v/+/v7//v7+//v7+//19fX/8PDw + /8LCwvosLCyTAAAAFwAAAAAAAAAAAAAACgAAAENfX1/S5OTk/vn5+f/+/v7/ + ///////////////////////////8/Pz/5ubm/l9fX9IAAABDAAAACgAAAAAA + AAAAAAAAAwAAABcAAABlYmJi3NLS0v3///////////////////////////// + ///V1dX9ZGRk3AAAAGUAAAAXAAAAAwAAAAAAAAAAAAAAAAAAAAUAAAAfAAAA + ZTMzM8KAgIDwv7+//O3t7f/t7e3/v7+//ICAgPAzMzPCAAAAZQAAAB8AAAAF + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwAAAEMAAAB3AAAAnwAAALMA + AACzAAAAnwAAAHcAAABDAAAAFwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAwAAAAoAAAAXAAAAJAAAAC4AAAAuAAAAJAAAABcAAAAKAAAA + AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + DgEAAAMAAAABABIAAAEBAAMAAAABABIAAAECAAMAAAAEAAAFxgEDAAMAAAAB + AAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMA + AAABAAQAAAEWAAMAAAABBxwAAAEXAAQAAAABAAAFEAEcAAMAAAABAAEAAAFS + AAMAAAABAAEAAAFTAAMAAAAEAAAFzodzAAcAAAM4AAAF1gAAAAAACAAIAAgA + CAABAAEAAQABAAADOGFwcGwCAAAAbW50clJHQiBYWVogB9UACgAQAAQABQAz + YWNzcEFQUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA0y1h + cHBspKaZrF1n3/yupoc/31HBfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAANclhZWgAAASAAAAAUZ1hZWgAAATQAAAAUYlhZWgAAAUgAAAAUd3Rw + dAAAAVwAAAAUY2hhZAAAAXAAAAAsclRSQwAAAZwAAAAOZ1RSQwAAAawAAAAO + YlRSQwAAAbwAAAAOdmNndAAAAcwAAAAwbmRpbgAAAfwAAAA4ZGVzYwAAAjQA + AACrY3BydAAAAuAAAAAtbW1vZAAAAxAAAAAoWFlaIAAAAAAAAG+NAAA9QgAA + AW5YWVogAAAAAAAAYOAAAKnfAAAazFhZWiAAAAAAAAAmaQAAGSEAALbkWFla + IAAAAAAAAPAYAAEAAAABF3tzZjMyAAAAAAABDwQAAAdY///yjgAACccAAPvu + ///7Vf///aoAAAO4AADACWN1cnYAAAAAAAAAAQHNAABjdXJ2AAAAAAAAAAEB + zQAAY3VydgAAAAAAAAABAc0AAHZjZ3QAAAAAAAAAAQAA5zYAAAAAAAEAAAAA + 5zYAAAAAAAEAAAAA5zYAAAAAAAEAAG5kaW4AAAAAAAAAMAAAokAAAFqAAABN + wAAAlMAAACYXAAAW2wAAT0AAAFSAAAH+NQAB/jUAAf41ZGVzYwAAAAAAAAAb + U3luY01hc3RlciA5MTJOIENhbGlicmF0ZWQAAAAAAAAAABsAUwB5AG4AYwBN + AGEAcwB0AGUAcgAgADkAMQAyAE4AIABDAGEAbABpAGIAcgBhAHQAZQBkAAAA + ABtTeW5jTWFzdGVyIDkxMk4gQ2FsaWJyYXRlZAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRleHQAAAAAQ29weXJpZ2h0IEFw + cGxlIENvbXB1dGVyLCBJbmMuLCAyMDAzAAAAAG1tb2QAAAAAAABMLQAAAR9N + SjE5vWVNgAAAAAAAAAAAAAAAAAAAAAA= + + + $classes + + NSMatrix + %NSMatrix + NSControl + NSView + NSResponder + NSObject + + $classname + NSMatrix + + {{17, 34}, {65, 17}} + + $class + + CF$UID + 35 + + NSCell + + CF$UID + 612 + + NSEnabled + + NSFrame + + CF$UID + 610 + + NSNextKeyView + + CF$UID + 611 + + NSNextResponder + + CF$UID + 563 + + NSSuperview + + CF$UID + 563 + + NSvFlags + 266 + + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 26 + + NSCellFlags + 67239424 + NSCellFlags2 + 272629760 + NSContents + + CF$UID + 609 + + NSControlView + + CF$UID + 571 + + NSSupport + + CF$UID + 23 + + NSTextColor + + CF$UID + 31 + + + Demo file + {{87, 32}, {174, 22}} + + $class + + CF$UID + 109 + + NSCell + + CF$UID + 615 + + NSEnabled + + NSFrame + + CF$UID + 613 + + NSNextKeyView + + CF$UID + 614 + + NSNextResponder + + CF$UID + 563 + + NSSuperview + + CF$UID + 563 + + NSvFlags + 265 + + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 89 + + NSCellFlags + -1804468671 + NSCellFlags2 + 272630784 + NSContents + + CF$UID + 40 + + NSControlView + + CF$UID + 607 + + NSDrawsBackground + + NSSupport + + CF$UID + 23 + + NSTextColor + + CF$UID + 541 + + + {{263, 24}, {87, 32}} + + $class + + CF$UID + 35 + + NSCell + + CF$UID + 619 + + NSEnabled + + NSFrame + + CF$UID + 617 + + NSNextKeyView + + CF$UID + 618 + + NSNextResponder + + CF$UID + 563 + + NSSuperview + + CF$UID + 563 + + NSvFlags + 268 + + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 23 + + NSButtonFlags + -2038284033 + NSButtonFlags2 + 1 + NSCellFlags + 67239424 + NSCellFlags2 + 134217728 + NSContents + + CF$UID + 545 + + NSControlView + + CF$UID + 611 + + NSKeyEquivalent + + CF$UID + 616 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 23 + + + + $class + + CF$UID + 6 + + NS.string + + + {{17, 2}, {133, 17}} + + $class + + CF$UID + 35 + + NSCell + + CF$UID + 622 + + NSEnabled + + NSFrame + + CF$UID + 621 + + NSNextResponder + + CF$UID + 563 + + NSSuperview + + CF$UID + 563 + + NSvFlags + 266 + + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 26 + + NSCellFlags + 67239424 + NSCellFlags2 + 272629760 + NSContents + + CF$UID + 620 + + NSControlView + + CF$UID + 614 + + NSSupport + + CF$UID + 23 + + NSTextColor + + CF$UID + 31 + + + Fast forward to level + {{155, 0}, {189, 22}} + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 89 + + NSCellFlags + -1804468671 + NSCellFlags2 + 272630784 + NSContents + + CF$UID + 40 + + NSControlView + + CF$UID + 618 + + NSDrawsBackground + + NSSupport + + CF$UID + 23 + + NSTextColor + + CF$UID + 541 + + + {{20, 194}, {364, 126}} + + $class + + CF$UID + 6 + + NS.string + NSView + + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 630 + + NSLabel + + CF$UID + 557 + + NSSource + + CF$UID + 626 + + + + $class + + CF$UID + 503 + + NSContentSize + + CF$UID + 627 + + NSDelegate + + CF$UID + 0 + + NSLeadingOffset + 0.0 + NSMaxContentSize + + CF$UID + 629 + + NSMinContentSize + + CF$UID + 628 + + NSNextResponder + + CF$UID + 0 + + NSParentWindow + + CF$UID + 0 + + NSPreferredEdge + 0 + NSTrailingOffset + 15 + + {280, 253} + {280, 166} + {600, 400} + + $class + + CF$UID + 556 + + NSClassName + + CF$UID + 680 + + NSExtension + + CF$UID + 555 + + NSFrame + + CF$UID + 679 + + NSNextResponder + + CF$UID + 507 + + NSSubviews + + CF$UID + 631 + + NSSuperview + + CF$UID + 507 + + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 632 + + + CF$UID + 636 + + + CF$UID + 641 + + + CF$UID + 646 + + + + + $class + + CF$UID + 35 + + NSCell + + CF$UID + 634 + + NSEnabled + + NSFrame + + CF$UID + 633 + + NSNextResponder + + CF$UID + 630 + + NSSuperview + + CF$UID + 630 + + NSvFlags + 264 + + {{17, 149}, {114, 17}} + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 26 + + NSCellFlags + 67239424 + NSCellFlags2 + 272629760 + NSContents + + CF$UID + 635 + + NSControlView + + CF$UID + 632 + + NSSupport + + CF$UID + 551 + + NSTextColor + + CF$UID + 31 + + + Wad Options + + $class + + CF$UID + 109 + + NSCell + + CF$UID + 638 + + NSEnabled + + NSFrame + + CF$UID + 637 + + NSNextResponder + + CF$UID + 630 + + NSSuperview + + CF$UID + 630 + + NSvFlags + 289 + + {{20, 0}, {24, 25}} + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSButtonFlags + -2038152961 + NSButtonFlags2 + 34 + NSCellFlags + 67239424 + NSCellFlags2 + 134217728 + NSContents + + CF$UID + 639 + + NSControlView + + CF$UID + 636 + + NSKeyEquivalent + + CF$UID + 640 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 551 + + + + + + $class + + CF$UID + 6 + + NS.string + + + + $class + + CF$UID + 109 + + NSCell + + CF$UID + 643 + + NSEnabled + + NSFrame + + CF$UID + 642 + + NSNextKeyView + + CF$UID + 636 + + NSNextResponder + + CF$UID + 630 + + NSSuperview + + CF$UID + 630 + + NSvFlags + 289 + + {{43, 0}, {24, 25}} + + $class + + CF$UID + 108 + + NSAlternateContents + + CF$UID + 40 + + NSAlternateImage + + CF$UID + 39 + + NSButtonFlags + -2038284033 + NSButtonFlags2 + 34 + NSCellFlags + 67239424 + NSCellFlags2 + 134217728 + NSContents + + CF$UID + 644 + + NSControlView + + CF$UID + 641 + + NSKeyEquivalent + + CF$UID + 645 + + NSPeriodicDelay + 200 + NSPeriodicInterval + 25 + NSSupport + + CF$UID + 551 + + + - + + $class + + CF$UID + 6 + + NS.string + + + + $class + + CF$UID + 493 + + NSContentView + + CF$UID + 648 + + NSFrame + + CF$UID + 678 + + NSHScroller + + CF$UID + 652 + + NSNextKeyView + + CF$UID + 648 + + NSNextResponder + + CF$UID + 630 + + NSScrollAmts + + QSAAAEEgAABBmAAAQZgAAA== + + NSSubviews + + CF$UID + 647 + + NSSuperview + + CF$UID + 630 + + NSVScroller + + CF$UID + 673 + + NSsFlags + 18 + NSvFlags + 274 + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 648 + + + CF$UID + 673 + + + CF$UID + 652 + + + + + $class + + CF$UID + 484 + + NSBGColor + + CF$UID + 161 + + NSDocView + + CF$UID + 650 + + NSFrame + + CF$UID + 672 + + NSNextKeyView + + CF$UID + 650 + + NSNextResponder + + CF$UID + 646 + + NSSubviews + + CF$UID + 649 + + NSSuperview + + CF$UID + 646 + + NScvFlags + 4 + NSvFlags + 2304 + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 650 + + + + + $class + + CF$UID + 671 + + NSBackgroundColor + + CF$UID + 91 + + NSColumnAutoresizingStyle + 4 + NSCornerView + + CF$UID + 653 + + NSDraggingSourceMaskForLocal + 15 + NSDraggingSourceMaskForNonLocal + 0 + NSEnabled + + NSFrameSize + + CF$UID + 651 + + NSGridColor + + CF$UID + 164 + + NSIntercellSpacingHeight + 2 + NSIntercellSpacingWidth + 3 + NSNextKeyView + + CF$UID + 652 + + NSNextResponder + + CF$UID + 648 + + NSRowHeight + 17 + NSSuperview + + CF$UID + 648 + + NSTableColumns + + CF$UID + 656 + + NSTvFlags + 440401920 + NSvFlags + 256 + + {223, 107} + + $class + + CF$UID + 488 + + NSAction + + CF$UID + 677 + + NSFrame + + CF$UID + 676 + + NSNextKeyView + + CF$UID + 648 + + NSNextResponder + + CF$UID + 646 + + NSPercent + 0.99047619104385376 + NSSuperview + + CF$UID + 646 + + NSTarget + + CF$UID + 646 + + NSsFlags + 1 + NSvFlags + 256 + + + $class + + CF$UID + 655 + + NSFrame + + CF$UID + 654 + + NSNextResponder + + CF$UID + 0 + + NSvFlags + 256 + + {{293, 0}, {16, 17}} + + $classes + + _NSCornerView + NSView + NSResponder + NSObject + + $classname + _NSCornerView + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 657 + + + CF$UID + 665 + + + + + $class + + CF$UID + 163 + + NSDataCell + + CF$UID + 663 + + NSHeaderCell + + CF$UID + 659 + + NSIdentifier + + CF$UID + 658 + + NSMaxWidth + 1000 + NSMinWidth + 40 + NSTableView + + CF$UID + 650 + + NSWidth + 40 + + Icon + + $class + + CF$UID + 159 + + NSBackgroundColor + + CF$UID + 660 + + NSCellFlags + 75628032 + NSCellFlags2 + 0 + NSContents + + CF$UID + 658 + + NSSupport + + CF$UID + 88 + + NSTextColor + + CF$UID + 661 + + + + $class + + CF$UID + 30 + + NSColorSpace + 3 + NSWhite + + MC4zMzMzMzI5OQA= + + + + $class + + CF$UID + 30 + + NSCatalogName + + CF$UID + 27 + + NSColor + + CF$UID + 33 + + NSColorName + + CF$UID + 662 + + NSColorSpace + 6 + + headerTextColor + + $class + + CF$UID + 664 + + NSAlign + 0 + NSAnimates + + NSCellFlags + 130560 + NSCellFlags2 + 33554432 + NSScale + 0 + NSStyle + 0 + + + $classes + + NSImageCell + %NSImageCell + NSCell + NSObject + + $classname + NSImageCell + + + $class + + CF$UID + 163 + + NSDataCell + + CF$UID + 670 + + NSHeaderCell + + CF$UID + 667 + + NSIdentifier + + CF$UID + 666 + + NSIsEditable + + NSIsResizeable + + NSMaxWidth + 1000 + NSMinWidth + 27.095703125 + NSResizingMask + 1 + NSTableView + + CF$UID + 650 + + NSWidth + 177.095703125 + + Path + + $class + + CF$UID + 159 + + NSBackgroundColor + + CF$UID + 668 + + NSCellFlags + 67239424 + NSCellFlags2 + 0 + NSContents + + CF$UID + 666 + + NSSupport + + CF$UID + 88 + + NSTextColor + + CF$UID + 661 + + + + $class + + CF$UID + 30 + + NSCatalogName + + CF$UID + 27 + + NSColor + + CF$UID + 91 + + NSColorName + + CF$UID + 669 + + NSColorSpace + 6 + + headerColor + + $class + + CF$UID + 34 + + NSBackgroundColor + + CF$UID + 89 + + NSCellFlags + 69336641 + NSCellFlags2 + 2560 + NSControlView + + CF$UID + 650 + + NSSupport + + CF$UID + 157 + + NSTextColor + + CF$UID + 31 + + + + $classes + + NSTableView + %NSTableView + NSControl + NSView + NSResponder + NSObject + + $classname + NSTableView + + {{1, 1}, {223, 107}} + + $class + + CF$UID + 488 + + NSAction + + CF$UID + 675 + + NSFrame + + CF$UID + 674 + + NSNextKeyView + + CF$UID + 641 + + NSNextResponder + + CF$UID + 646 + + NSPercent + 0.56315791606903076 + NSSuperview + + CF$UID + 646 + + NSTarget + + CF$UID + 646 + + NSvFlags + 256 + + {{224, 1}, {15, 107}} + _doScroller: + {{-100, -100}, {307, 15}} + _doScroller: + {{20, 32}, {240, 109}} + {{20, 20}, {280, 166}} + NSView + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 8 + + NSLabel + + CF$UID + 504 + + NSSource + + CF$UID + 626 + + + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 8 + + NSLabel + + CF$UID + 504 + + NSSource + + CF$UID + 559 + + + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 513 + + NSLabel + + CF$UID + 684 + + NSSource + + CF$UID + 340 + + + disableJoystickButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 509 + + NSLabel + + CF$UID + 686 + + NSSource + + CF$UID + 340 + + + disableGraphicsButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 517 + + NSLabel + + CF$UID + 688 + + NSSource + + CF$UID + 340 + + + disableMouseButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 519 + + NSLabel + + CF$UID + 690 + + NSSource + + CF$UID + 340 + + + disableSoundButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 523 + + NSLabel + + CF$UID + 692 + + NSSource + + CF$UID + 340 + + + disableSoundEffectsButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 527 + + NSLabel + + CF$UID + 694 + + NSSource + + CF$UID + 340 + + + disableMusicButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 573 + + NSLabel + + CF$UID + 696 + + NSSource + + CF$UID + 340 + + + noDemoButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 590 + + NSLabel + + CF$UID + 698 + + NSSource + + CF$UID + 340 + + + playDemoButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 578 + + NSLabel + + CF$UID + 700 + + NSSource + + CF$UID + 340 + + + fastDemoButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 592 + + NSLabel + + CF$UID + 702 + + NSSource + + CF$UID + 340 + + + timeDemoButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 618 + + NSLabel + + CF$UID + 704 + + NSSource + + CF$UID + 340 + + + ffToLevelField + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 569 + + NSLabel + + CF$UID + 706 + + NSSource + + CF$UID + 340 + + + demoMatrix + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 340 + + NSLabel + + CF$UID + 708 + + NSSource + + CF$UID + 519 + + + disableSoundClicked: + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 340 + + NSLabel + + CF$UID + 710 + + NSSource + + CF$UID + 569 + + + demoButtonClicked: + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 499 + + NSLabel + + CF$UID + 712 + + NSSource + + CF$UID + 340 + + + debugDrawer + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 559 + + NSLabel + + CF$UID + 714 + + NSSource + + CF$UID + 340 + + + demoDrawer + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 626 + + NSLabel + + CF$UID + 716 + + NSSource + + CF$UID + 340 + + + wadDrawer + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 718 + + NSLabel + + CF$UID + 720 + + NSSource + + CF$UID + 650 + + + + $class + + CF$UID + 4 + + NSClassName + + CF$UID + 719 + + + WadViewController + dataSource + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 718 + + NSLabel + + CF$UID + 342 + + NSSource + + CF$UID + 650 + + + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 718 + + NSLabel + + CF$UID + 723 + + NSSource + + CF$UID + 636 + + + add: + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 718 + + NSLabel + + CF$UID + 725 + + NSSource + + CF$UID + 641 + + + remove: + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 641 + + NSLabel + + CF$UID + 727 + + NSSource + + CF$UID + 718 + + + removeButton + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 650 + + NSLabel + + CF$UID + 729 + + NSSource + + CF$UID + 718 + + + view + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 718 + + NSLabel + + CF$UID + 731 + + NSSource + + CF$UID + 340 + + + wadViewController + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 626 + + NSLabel + + CF$UID + 733 + + NSSource + + CF$UID + 129 + + + drawer + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 559 + + NSLabel + + CF$UID + 733 + + NSSource + + CF$UID + 135 + + + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 499 + + NSLabel + + CF$UID + 733 + + NSSource + + CF$UID + 119 + + + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 119 + + NSLabel + + CF$UID + 342 + + NSSource + + CF$UID + 499 + + + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 135 + + NSLabel + + CF$UID + 342 + + NSSource + + CF$UID + 559 + + + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 129 + + NSLabel + + CF$UID + 342 + + NSSource + + CF$UID + 626 + + + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 626 + + NSLabel + + CF$UID + 740 + + NSSource + + CF$UID + 129 + + + toggle: + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 559 + + NSLabel + + CF$UID + 740 + + NSSource + + CF$UID + 135 + + + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 499 + + NSLabel + + CF$UID + 740 + + NSSource + + CF$UID + 119 + + + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 744 + + NSLabel + + CF$UID + 746 + + NSSource + + CF$UID + 340 + + + + $class + + CF$UID + 4 + + NSClassName + + CF$UID + 745 + + + ConsoleController + consoleController + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 441 + + NSLabel + + CF$UID + 421 + + NSSource + + CF$UID + 744 + + + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 452 + + NSLabel + + CF$UID + 749 + + NSSource + + CF$UID + 744 + + + textView + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 744 + + NSLabel + + CF$UID + 342 + + NSSource + + CF$UID + 441 + + + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 200 + + NSLabel + + CF$UID + 752 + + NSSource + + CF$UID + 8 + + + initialFirstResponder + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 754 + + NSLabel + + CF$UID + 756 + + NSSource + + CF$UID + 340 + + + + $class + + CF$UID + 4 + + NSClassName + + CF$UID + 755 + + + FileButtonController + demoFileButtonController + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 758 + + NSLabel + + CF$UID + 759 + + NSSource + + CF$UID + 340 + + + + $class + + CF$UID + 4 + + NSClassName + + CF$UID + 755 + + + configFileButtonController + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 611 + + NSLabel + + CF$UID + 761 + + NSSource + + CF$UID + 754 + + + button + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 607 + + NSLabel + + CF$UID + 763 + + NSSource + + CF$UID + 754 + + + field + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 539 + + NSLabel + + CF$UID + 761 + + NSSource + + CF$UID + 758 + + + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 535 + + NSLabel + + CF$UID + 763 + + NSSource + + CF$UID + 758 + + + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 754 + + NSLabel + + CF$UID + 767 + + NSSource + + CF$UID + 611 + + + buttonClicked: + + $class + + CF$UID + 218 + + NSDestination + + CF$UID + 758 + + NSLabel + + CF$UID + 767 + + NSSource + + CF$UID + 539 + + + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 145 + + NSLabel + + CF$UID + 770 + + NSSource + + CF$UID + 340 + + + resolutionComboBox + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 772 + + NSLabel + + CF$UID + 720 + + NSSource + + CF$UID + 145 + + + + $class + + CF$UID + 4 + + NSClassName + + CF$UID + 773 + + + ResolutionDataSource + + $class + + CF$UID + 302 + + NSDestination + + CF$UID + 175 + + NSLabel + + CF$UID + 775 + + NSSource + + CF$UID + 340 + + + graphicsModeComboBox + + $class + + CF$UID + 587 + + NS.objects + + + CF$UID + 58 + + + CF$UID + 641 + + + CF$UID + 317 + + + CF$UID + 370 + + + CF$UID + 115 + + + CF$UID + 381 + + + CF$UID + 295 + + + CF$UID + 650 + + + CF$UID + 13 + + + CF$UID + 527 + + + CF$UID + 663 + + + CF$UID + 513 + + + CF$UID + 611 + + + CF$UID + 73 + + + CF$UID + 340 + + + CF$UID + 319 + + + CF$UID + 371 + + + CF$UID + 286 + + + CF$UID + 665 + + + CF$UID + 395 + + + CF$UID + 297 + + + CF$UID + 559 + + + CF$UID + 366 + + + CF$UID + 74 + + + CF$UID + 36 + + + CF$UID + 96 + + + CF$UID + 374 + + + CF$UID + 321 + + + CF$UID + 446 + + + CF$UID + 129 + + + CF$UID + 367 + + + CF$UID + 535 + + + CF$UID + 406 + + + CF$UID + 119 + + + CF$UID + 299 + + + CF$UID + 15 + + + CF$UID + 213 + + + CF$UID + 507 + + + CF$UID + 78 + + + CF$UID + 171 + + + CF$UID + 247 + + + CF$UID + 670 + + + CF$UID + 417 + + + CF$UID + 214 + + + CF$UID + 614 + + + CF$UID + 632 + + + CF$UID + 362 + + + CF$UID + 569 + + + CF$UID + 145 + + + CF$UID + 590 + + + CF$UID + 80 + + + CF$UID + 282 + + + CF$UID + 248 + + + CF$UID + 323 + + + CF$UID + 98 + + + CF$UID + 268 + + + CF$UID + 448 + + + CF$UID + 718 + + + CF$UID + 539 + + + CF$UID + 509 + + + CF$UID + 410 + + + CF$UID + 325 + + + CF$UID + 258 + + + CF$UID + 17 + + + CF$UID + 378 + + + CF$UID + 563 + + + CF$UID + 646 + + + CF$UID + 304 + + + CF$UID + 630 + + + CF$UID + 400 + + + CF$UID + 306 + + + CF$UID + 618 + + + CF$UID + 327 + + + CF$UID + 390 + + + CF$UID + 403 + + + CF$UID + 308 + + + CF$UID + 175 + + + CF$UID + 100 + + + CF$UID + 135 + + + CF$UID + 398 + + + CF$UID + 239 + + + CF$UID + 329 + + + CF$UID + 439 + + + CF$UID + 394 + + + CF$UID + 744 + + + CF$UID + 571 + + + CF$UID + 519 + + + CF$UID + 387 + + + CF$UID + 636 + + + CF$UID + 234 + + + CF$UID + 310 + + + CF$UID + 195 + + + CF$UID + 565 + + + CF$UID + 573 + + + CF$UID + 331 + + + CF$UID + 754 + + + CF$UID + 273 + + + CF$UID + 8 + + + CF$UID + 758 + + + CF$UID + 415 + + + CF$UID + 243 + + + CF$UID + 312 + + + CF$UID + 499 + + + CF$UID + 452 + + + CF$UID + 42 + + + CF$UID + 506 + + + CF$UID + 69 + + + CF$UID + 517 + + + CF$UID + 19 + + + CF$UID + 111 + + + CF$UID + 578 + + + CF$UID + 200 + + + CF$UID + 333 + + + CF$UID + 253 + + + CF$UID + 359 + + + CF$UID + 592 + + + CF$UID + 379 + + + CF$UID + 314 + + + CF$UID + 384 + + + CF$UID + 43 + + + CF$UID + 220 + + + CF$UID + 94 + + + CF$UID + 626 + + + CF$UID + 772 + + + CF$UID + 607 + + + CF$UID + 523 + + + CF$UID + 289 + + + CF$UID + 779 + + + CF$UID + 65 + + + CF$UID + 335 + + + CF$UID + 278 + + + CF$UID + 291 + + + CF$UID + 55 + + + CF$UID + 657 + + + CF$UID + 225 + + + CF$UID + 441 + + + CF$UID + 315 + + + CF$UID + 230 + + + CF$UID + 141 + + + CF$UID + 293 + + + CF$UID + 547 + + + CF$UID + 337 + + + CF$UID + 224 + + + CF$UID + 263 + + + CF$UID + 533 + + + CF$UID + 412 + + + + + $class + + CF$UID + 61 + + NS.objects + + + CF$UID + 506 + + + CF$UID + 563 + + + CF$UID + 630 + + + + {{1, 9}, {410, 525}} + + $class + + CF$UID + 209 + + NSMaxSize + + CF$UID + 786 + + NSMinSize + + CF$UID + 785 + + NSScreenRect + + CF$UID + 784 + + NSViewClass + + CF$UID + 783 + + NSWTFlags + 1886912512 + NSWindowBacking + 2 + NSWindowClass + + CF$UID + 782 + + NSWindowRect + + CF$UID + 780 + + NSWindowStyleMask + 14 + NSWindowTitle + + CF$UID + 781 + + NSWindowView + + CF$UID + 507 + + + {{3, 455}, {410, 525}} + Drawer Views + + $class + + CF$UID + 6 + + NS.string + NSWindow + + + $class + + CF$UID + 6 + + NS.string + View + + {{0, 0}, {1280, 1002}} + {410, 231} + {3.40282e+38, 3.40282e+38} + + $class + + CF$UID + 587 + + NS.objects + + + CF$UID + 43 + + + CF$UID + 630 + + + CF$UID + 304 + + + CF$UID + 225 + + + CF$UID + 96 + + + CF$UID + 359 + + + CF$UID + 286 + + + CF$UID + 646 + + + CF$UID + 8 + + + CF$UID + 506 + + + CF$UID + 657 + + + CF$UID + 506 + + + CF$UID + 563 + + + CF$UID + 74 + + + CF$UID + 2 + + + CF$UID + 304 + + + CF$UID + 225 + + + CF$UID + 2 + + + CF$UID + 650 + + + CF$UID + 359 + + + CF$UID + 286 + + + CF$UID + 2 + + + CF$UID + 225 + + + CF$UID + 69 + + + CF$UID + 17 + + + CF$UID + 195 + + + CF$UID + 371 + + + CF$UID + 304 + + + CF$UID + 441 + + + CF$UID + 96 + + + CF$UID + 225 + + + CF$UID + 506 + + + CF$UID + 359 + + + CF$UID + 96 + + + CF$UID + 286 + + + CF$UID + 13 + + + CF$UID + 214 + + + CF$UID + 779 + + + CF$UID + 74 + + + CF$UID + 96 + + + CF$UID + 248 + + + CF$UID + 665 + + + CF$UID + 415 + + + CF$UID + 406 + + + CF$UID + 563 + + + CF$UID + 630 + + + CF$UID + 359 + + + CF$UID + 563 + + + CF$UID + 96 + + + CF$UID + 569 + + + CF$UID + 74 + + + CF$UID + 214 + + + CF$UID + 390 + + + CF$UID + 304 + + + CF$UID + 96 + + + CF$UID + 248 + + + CF$UID + 446 + + + CF$UID + 2 + + + CF$UID + 506 + + + CF$UID + 506 + + + CF$UID + 214 + + + CF$UID + 304 + + + CF$UID + 248 + + + CF$UID + 15 + + + CF$UID + 225 + + + CF$UID + 507 + + + CF$UID + 630 + + + CF$UID + 2 + + + CF$UID + 507 + + + CF$UID + 398 + + + CF$UID + 304 + + + CF$UID + 563 + + + CF$UID + 304 + + + CF$UID + 359 + + + CF$UID + 398 + + + CF$UID + 304 + + + CF$UID + 96 + + + CF$UID + 96 + + + CF$UID + 96 + + + CF$UID + 395 + + + CF$UID + 225 + + + CF$UID + 304 + + + CF$UID + 2 + + + CF$UID + 248 + + + CF$UID + 2 + + + CF$UID + 563 + + + CF$UID + 506 + + + CF$UID + 384 + + + CF$UID + 630 + + + CF$UID + 225 + + + CF$UID + 304 + + + CF$UID + 94 + + + CF$UID + 563 + + + CF$UID + 569 + + + CF$UID + 304 + + + CF$UID + 2 + + + CF$UID + 248 + + + CF$UID + 2 + + + CF$UID + 2 + + + CF$UID + 412 + + + CF$UID + 225 + + + CF$UID + 304 + + + CF$UID + 2 + + + CF$UID + 448 + + + CF$UID + 43 + + + CF$UID + 507 + + + CF$UID + 17 + + + CF$UID + 506 + + + CF$UID + 17 + + + CF$UID + 96 + + + CF$UID + 569 + + + CF$UID + 13 + + + CF$UID + 304 + + + CF$UID + 248 + + + CF$UID + 2 + + + CF$UID + 569 + + + CF$UID + 225 + + + CF$UID + 304 + + + CF$UID + 381 + + + CF$UID + 36 + + + CF$UID + 214 + + + CF$UID + 13 + + + CF$UID + 2 + + + CF$UID + 2 + + + CF$UID + 563 + + + CF$UID + 506 + + + CF$UID + 286 + + + CF$UID + 2 + + + CF$UID + 17 + + + CF$UID + 304 + + + CF$UID + 248 + + + CF$UID + 286 + + + CF$UID + 43 + + + CF$UID + 650 + + + CF$UID + 362 + + + CF$UID + 2 + + + CF$UID + 304 + + + CF$UID + 225 + + + CF$UID + 96 + + + CF$UID + 286 + + + CF$UID + 506 + + + CF$UID + 304 + + + CF$UID + 225 + + + CF$UID + 248 + + + CF$UID + 506 + + + CF$UID + 359 + + + + + $class + + CF$UID + 587 + + NS.objects + + + CF$UID + 15 + + + CF$UID + 563 + + + CF$UID + 195 + + + CF$UID + 69 + + + CF$UID + 337 + + + CF$UID + 772 + + + CF$UID + 650 + + + CF$UID + 779 + + + CF$UID + 224 + + + CF$UID + 135 + + + CF$UID + 247 + + + CF$UID + 412 + + + CF$UID + 278 + + + CF$UID + 646 + + + CF$UID + 519 + + + CF$UID + 607 + + + CF$UID + 200 + + + CF$UID + 641 + + + CF$UID + 340 + + + CF$UID + 499 + + + CF$UID + 509 + + + CF$UID + 618 + + + CF$UID + 111 + + + CF$UID + 263 + + + CF$UID + 248 + + + CF$UID + 632 + + + CF$UID + 513 + + + CF$UID + 452 + + + CF$UID + 96 + + + CF$UID + 145 + + + CF$UID + 268 + + + CF$UID + 565 + + + CF$UID + 533 + + + CF$UID + 569 + + + CF$UID + 17 + + + CF$UID + 718 + + + CF$UID + 630 + + + CF$UID + 42 + + + CF$UID + 65 + + + CF$UID + 657 + + + CF$UID + 258 + + + CF$UID + 286 + + + CF$UID + 506 + + + CF$UID + 8 + + + CF$UID + 36 + + + CF$UID + 129 + + + CF$UID + 523 + + + CF$UID + 441 + + + CF$UID + 611 + + + CF$UID + 73 + + + CF$UID + 171 + + + CF$UID + 43 + + + CF$UID + 367 + + + CF$UID + 98 + + + CF$UID + 359 + + + CF$UID + 115 + + + CF$UID + 439 + + + CF$UID + 289 + + + CF$UID + 55 + + + CF$UID + 535 + + + CF$UID + 74 + + + CF$UID + 304 + + + CF$UID + 394 + + + CF$UID + 141 + + + CF$UID + 744 + + + CF$UID + 417 + + + CF$UID + 94 + + + CF$UID + 517 + + + CF$UID + 308 + + + CF$UID + 636 + + + CF$UID + 78 + + + CF$UID + 58 + + + CF$UID + 571 + + + CF$UID + 539 + + + CF$UID + 19 + + + CF$UID + 754 + + + CF$UID + 415 + + + CF$UID + 273 + + + CF$UID + 527 + + + CF$UID + 626 + + + CF$UID + 758 + + + CF$UID + 175 + + + CF$UID + 547 + + + CF$UID + 614 + + + CF$UID + 448 + + + CF$UID + 100 + + + CF$UID + 119 + + + CF$UID + 80 + + + CF$UID + 390 + + + CF$UID + 2 + + + CF$UID + 559 + + + CF$UID + 253 + + + + + $class + + CF$UID + 587 + + NS.objects + + + CF$UID + 790 + + + CF$UID + 791 + + + CF$UID + 792 + + + CF$UID + 793 + + + CF$UID + 794 + + + CF$UID + 773 + + + CF$UID + 795 + + + CF$UID + 781 + + + CF$UID + 796 + + + CF$UID + 797 + + + CF$UID + 798 + + + CF$UID + 799 + + + CF$UID + 800 + + + CF$UID + 801 + + + CF$UID + 802 + + + CF$UID + 803 + + + CF$UID + 804 + + + CF$UID + 805 + + + CF$UID + 341 + + + CF$UID + 806 + + + CF$UID + 807 + + + CF$UID + 808 + + + CF$UID + 809 + + + CF$UID + 810 + + + CF$UID + 811 + + + CF$UID + 812 + + + CF$UID + 813 + + + CF$UID + 814 + + + CF$UID + 680 + + + CF$UID + 815 + + + CF$UID + 816 + + + CF$UID + 812 + + + CF$UID + 817 + + + CF$UID + 818 + + + CF$UID + 680 + + + CF$UID + 719 + + + CF$UID + 819 + + + CF$UID + 820 + + + CF$UID + 817 + + + CF$UID + 821 + + + CF$UID + 822 + + + CF$UID + 823 + + + CF$UID + 824 + + + CF$UID + 407 + + + CF$UID + 825 + + + CF$UID + 805 + + + CF$UID + 826 + + + CF$UID + 827 + + + CF$UID + 804 + + + CF$UID + 820 + + + CF$UID + 817 + + + CF$UID + 828 + + + CF$UID + 829 + + + CF$UID + 830 + + + CF$UID + 831 + + + CF$UID + 832 + + + CF$UID + 833 + + + CF$UID + 820 + + + CF$UID + 794 + + + CF$UID + 803 + + + CF$UID + 828 + + + CF$UID + 834 + + + CF$UID + 820 + + + CF$UID + 812 + + + CF$UID + 745 + + + CF$UID + 835 + + + CF$UID + 836 + + + CF$UID + 837 + + + CF$UID + 820 + + + CF$UID + 804 + + + CF$UID + 794 + + + CF$UID + 816 + + + CF$UID + 817 + + + CF$UID + 804 + + + CF$UID + 812 + + + CF$UID + 838 + + + CF$UID + 839 + + + CF$UID + 794 + + + CF$UID + 840 + + + CF$UID + 841 + + + CF$UID + 842 + + + CF$UID + 843 + + + CF$UID + 812 + + + CF$UID + 844 + + + CF$UID + 845 + + + CF$UID + 846 + + + CF$UID + 804 + + + CF$UID + 816 + + + CF$UID + 820 + + + CF$UID + 847 + + + CF$UID + 848 + + + CF$UID + 849 + + + + NSBox + View1 + NSTabViewItem + NSPopUpButton1 + NSMenuItem1 + NSTableView + 1111 + NSButton2 + NSMenuItem3 + + $class + + CF$UID + 6 + + NS.string + 1 + + NSMenuItem4 + NSScrollView1 + NSButton4 + NSTextField + NSButton + NSButton1 + DebugDrawer + NSButton41 + NSTextField1 + NSButton48 + NSMenuItem10 + NSMenu + NSTextField2 + NSButton42 + NSTextView + NSComboBox + NSMenuItem2 + NSTextField21 + NSMatrix1 + View11 + NSMenuItem + NSTableColumn + NSMenuItem11 + GameMenu + View + NSPopUpButton + NSButton44 + ConsoleWindow + PopUpList + 121 + NSButton46 + + $class + + CF$UID + 6 + + NS.string + MainMenu + + NSButton49 + ConsoleWindowController + CompatMenu + + $class + + CF$UID + 6 + + NS.string + + + NSTabView + NSButton43 + DemoFileButtonController + + $class + + CF$UID + 6 + + NS.string + 2 + + NSButton45 + WadDrawer + ConfigFileButtonController + NSComboBox1 + NSTextField211 + NSScrollView2 + NSButton47 + + $class + + CF$UID + 6 + + NS.string + File's Owner + + DemoDrawer + NSMenuItem9 + + $class + + CF$UID + 587 + + NS.objects + + + CF$UID + 129 + + + CF$UID + 119 + + + CF$UID + 135 + + + + + $class + + CF$UID + 587 + + NS.objects + + + CF$UID + 122 + + + CF$UID + 122 + + + CF$UID + 122 + + + + + $class + + CF$UID + 587 + + NS.objects + + + CF$UID + 737 + + + CF$UID + 304 + + + CF$UID + 381 + + + CF$UID + 374 + + + CF$UID + 417 + + + CF$UID + 738 + + + CF$UID + 446 + + + CF$UID + 272 + + + CF$UID + 403 + + + CF$UID + 43 + + + CF$UID + 315 + + + CF$UID + 739 + + + CF$UID + 632 + + + CF$UID + 258 + + + CF$UID + 339 + + + CF$UID + 306 + + + CF$UID + 539 + + + CF$UID + 73 + + + CF$UID + 195 + + + CF$UID + 213 + + + CF$UID + 741 + + + CF$UID + 323 + + + CF$UID + 234 + + + CF$UID + 219 + + + CF$UID + 278 + + + CF$UID + 683 + + + CF$UID + 523 + + + CF$UID + 742 + + + CF$UID + 558 + + + CF$UID + 685 + + + CF$UID + 15 + + + CF$UID + 297 + + + CF$UID + 743 + + + CF$UID + 687 + + + CF$UID + 333 + + + CF$UID + 744 + + + CF$UID + 98 + + + CF$UID + 308 + + + CF$UID + 420 + + + CF$UID + 689 + + + CF$UID + 214 + + + CF$UID + 513 + + + CF$UID + 747 + + + CF$UID + 607 + + + CF$UID + 422 + + + CF$UID + 55 + + + CF$UID + 111 + + + CF$UID + 691 + + + CF$UID + 748 + + + CF$UID + 670 + + + CF$UID + 65 + + + CF$UID + 424 + + + CF$UID + 693 + + + CF$UID + 229 + + + CF$UID + 317 + + + CF$UID + 119 + + + CF$UID + 547 + + + CF$UID + 224 + + + CF$UID + 750 + + + CF$UID + 69 + + + CF$UID + 357 + + + CF$UID + 426 + + + CF$UID + 507 + + + CF$UID + 695 + + + CF$UID + 614 + + + CF$UID + 74 + + + CF$UID + 212 + + + CF$UID + 751 + + + CF$UID + 325 + + + CF$UID + 243 + + + CF$UID + 428 + + + CF$UID + 358 + + + CF$UID + 263 + + + CF$UID + 697 + + + CF$UID + 650 + + + CF$UID + 753 + + + CF$UID + 430 + + + CF$UID + 359 + + + CF$UID + 699 + + + CF$UID + 299 + + + CF$UID + 565 + + + CF$UID + 754 + + + CF$UID + 8 + + + CF$UID + 432 + + + CF$UID + 701 + + + CF$UID + 335 + + + CF$UID + 506 + + + CF$UID + 757 + + + CF$UID + 398 + + + CF$UID + 19 + + + CF$UID + 145 + + + CF$UID + 310 + + + CF$UID + 434 + + + CF$UID + 384 + + + CF$UID + 703 + + + CF$UID + 657 + + + CF$UID + 758 + + + CF$UID + 362 + + + CF$UID + 252 + + + CF$UID + 535 + + + CF$UID + 436 + + + CF$UID + 58 + + + CF$UID + 705 + + + CF$UID + 36 + + + CF$UID + 233 + + + CF$UID + 760 + + + CF$UID + 394 + + + CF$UID + 438 + + + CF$UID + 289 + + + CF$UID + 517 + + + CF$UID + 707 + + + CF$UID + 282 + + + CF$UID + 319 + + + CF$UID + 78 + + + CF$UID + 238 + + + CF$UID + 762 + + + CF$UID + 412 + + + CF$UID + 452 + + + CF$UID + 709 + + + CF$UID + 242 + + + CF$UID + 141 + + + CF$UID + 764 + + + CF$UID + 327 + + + CF$UID + 379 + + + CF$UID + 448 + + + CF$UID + 277 + + + CF$UID + 711 + + + CF$UID + 345 + + + CF$UID + 246 + + + CF$UID + 2 + + + CF$UID + 765 + + + CF$UID + 281 + + + CF$UID + 713 + + + CF$UID + 247 + + + CF$UID + 347 + + + CF$UID + 395 + + + CF$UID + 766 + + + CF$UID + 200 + + + CF$UID + 257 + + + CF$UID + 135 + + + CF$UID + 681 + + + CF$UID + 370 + + + CF$UID + 519 + + + CF$UID + 285 + + + CF$UID + 715 + + + CF$UID + 337 + + + CF$UID + 291 + + + CF$UID + 768 + + + CF$UID + 312 + + + CF$UID + 439 + + + CF$UID + 378 + + + CF$UID + 286 + + + CF$UID + 717 + + + CF$UID + 527 + + + CF$UID + 578 + + + CF$UID + 769 + + + CF$UID + 387 + + + CF$UID + 509 + + + CF$UID + 718 + + + CF$UID + 499 + + + CF$UID + 571 + + + CF$UID + 630 + + + CF$UID + 129 + + + CF$UID + 771 + + + CF$UID + 573 + + + CF$UID + 273 + + + CF$UID + 349 + + + CF$UID + 721 + + + CF$UID + 410 + + + CF$UID + 321 + + + CF$UID + 17 + + + CF$UID + 80 + + + CF$UID + 590 + + + CF$UID + 772 + + + CF$UID + 100 + + + CF$UID + 13 + + + CF$UID + 293 + + + CF$UID + 722 + + + CF$UID + 351 + + + CF$UID + 262 + + + CF$UID + 774 + + + CF$UID + 329 + + + CF$UID + 724 + + + CF$UID + 353 + + + CF$UID + 115 + + + CF$UID + 96 + + + CF$UID + 779 + + + CF$UID + 303 + + + CF$UID + 366 + + + CF$UID + 611 + + + CF$UID + 726 + + + CF$UID + 354 + + + CF$UID + 663 + + + CF$UID + 248 + + + CF$UID + 390 + + + CF$UID + 371 + + + CF$UID + 223 + + + CF$UID + 356 + + + CF$UID + 728 + + + CF$UID + 626 + + + CF$UID + 220 + + + CF$UID + 559 + + + CF$UID + 42 + + + CF$UID + 314 + + + CF$UID + 730 + + + CF$UID + 225 + + + CF$UID + 618 + + + CF$UID + 268 + + + CF$UID + 636 + + + CF$UID + 592 + + + CF$UID + 175 + + + CF$UID + 732 + + + CF$UID + 665 + + + CF$UID + 533 + + + CF$UID + 267 + + + CF$UID + 682 + + + CF$UID + 253 + + + CF$UID + 367 + + + CF$UID + 415 + + + CF$UID + 641 + + + CF$UID + 563 + + + CF$UID + 734 + + + CF$UID + 340 + + + CF$UID + 230 + + + CF$UID + 406 + + + CF$UID + 498 + + + CF$UID + 441 + + + CF$UID + 400 + + + CF$UID + 505 + + + CF$UID + 94 + + + CF$UID + 569 + + + CF$UID + 295 + + + CF$UID + 735 + + + CF$UID + 343 + + + CF$UID + 646 + + + CF$UID + 171 + + + CF$UID + 331 + + + CF$UID + 625 + + + CF$UID + 736 + + + CF$UID + 239 + + + + + $class + + CF$UID + 587 + + NS.objects + + + CF$UID + 854 + + + CF$UID + 855 + + + CF$UID + 856 + + + CF$UID + 857 + + + CF$UID + 858 + + + CF$UID + 859 + + + CF$UID + 860 + + + CF$UID + 861 + + + CF$UID + 862 + + + CF$UID + 863 + + + CF$UID + 864 + + + CF$UID + 865 + + + CF$UID + 866 + + + CF$UID + 867 + + + CF$UID + 868 + + + CF$UID + 869 + + + CF$UID + 870 + + + CF$UID + 871 + + + CF$UID + 872 + + + CF$UID + 873 + + + CF$UID + 874 + + + CF$UID + 875 + + + CF$UID + 876 + + + CF$UID + 877 + + + CF$UID + 878 + + + CF$UID + 879 + + + CF$UID + 880 + + + CF$UID + 881 + + + CF$UID + 882 + + + CF$UID + 883 + + + CF$UID + 884 + + + CF$UID + 885 + + + CF$UID + 886 + + + CF$UID + 887 + + + CF$UID + 888 + + + CF$UID + 889 + + + CF$UID + 890 + + + CF$UID + 891 + + + CF$UID + 892 + + + CF$UID + 893 + + + CF$UID + 894 + + + CF$UID + 895 + + + CF$UID + 896 + + + CF$UID + 897 + + + CF$UID + 898 + + + CF$UID + 899 + + + CF$UID + 900 + + + CF$UID + 901 + + + CF$UID + 902 + + + CF$UID + 903 + + + CF$UID + 904 + + + CF$UID + 905 + + + CF$UID + 906 + + + CF$UID + 907 + + + CF$UID + 908 + + + CF$UID + 909 + + + CF$UID + 910 + + + CF$UID + 911 + + + CF$UID + 912 + + + CF$UID + 913 + + + CF$UID + 914 + + + CF$UID + 915 + + + CF$UID + 916 + + + CF$UID + 917 + + + CF$UID + 918 + + + CF$UID + 919 + + + CF$UID + 920 + + + CF$UID + 921 + + + CF$UID + 922 + + + CF$UID + 923 + + + CF$UID + 924 + + + CF$UID + 925 + + + CF$UID + 926 + + + CF$UID + 927 + + + CF$UID + 928 + + + CF$UID + 929 + + + CF$UID + 930 + + + CF$UID + 931 + + + CF$UID + 932 + + + CF$UID + 933 + + + CF$UID + 934 + + + CF$UID + 935 + + + CF$UID + 936 + + + CF$UID + 937 + + + CF$UID + 938 + + + CF$UID + 939 + + + CF$UID + 940 + + + CF$UID + 941 + + + CF$UID + 942 + + + CF$UID + 943 + + + CF$UID + 944 + + + CF$UID + 945 + + + CF$UID + 946 + + + CF$UID + 947 + + + CF$UID + 948 + + + CF$UID + 949 + + + CF$UID + 950 + + + CF$UID + 951 + + + CF$UID + 952 + + + CF$UID + 953 + + + CF$UID + 954 + + + CF$UID + 955 + + + CF$UID + 956 + + + CF$UID + 957 + + + CF$UID + 958 + + + CF$UID + 959 + + + CF$UID + 960 + + + CF$UID + 961 + + + CF$UID + 962 + + + CF$UID + 963 + + + CF$UID + 964 + + + CF$UID + 965 + + + CF$UID + 966 + + + CF$UID + 967 + + + CF$UID + 968 + + + CF$UID + 969 + + + CF$UID + 970 + + + CF$UID + 971 + + + CF$UID + 972 + + + CF$UID + 973 + + + CF$UID + 974 + + + CF$UID + 975 + + + CF$UID + 976 + + + CF$UID + 977 + + + CF$UID + 978 + + + CF$UID + 979 + + + CF$UID + 980 + + + CF$UID + 981 + + + CF$UID + 982 + + + CF$UID + 474 + + + CF$UID + 983 + + + CF$UID + 984 + + + CF$UID + 985 + + + CF$UID + 986 + + + CF$UID + 987 + + + CF$UID + 988 + + + CF$UID + 989 + + + CF$UID + 990 + + + CF$UID + 991 + + + CF$UID + 992 + + + CF$UID + 993 + + + CF$UID + 994 + + + CF$UID + 995 + + + CF$UID + 996 + + + CF$UID + 997 + + + CF$UID + 998 + + + CF$UID + 999 + + + CF$UID + 1000 + + + CF$UID + 1001 + + + CF$UID + 1002 + + + CF$UID + 1003 + + + CF$UID + 1004 + + + CF$UID + 1005 + + + CF$UID + 1006 + + + CF$UID + 1007 + + + CF$UID + 1008 + + + CF$UID + 1009 + + + CF$UID + 1010 + + + CF$UID + 1011 + + + CF$UID + 1012 + + + CF$UID + 1013 + + + CF$UID + 1014 + + + CF$UID + 1015 + + + CF$UID + 1016 + + + CF$UID + 1017 + + + CF$UID + 1018 + + + CF$UID + 1019 + + + CF$UID + 1020 + + + CF$UID + 1021 + + + CF$UID + 1022 + + + CF$UID + 1023 + + + CF$UID + 1024 + + + CF$UID + 1025 + + + CF$UID + 1026 + + + CF$UID + 1027 + + + CF$UID + 1028 + + + CF$UID + 1029 + + + CF$UID + 1030 + + + CF$UID + 1031 + + + CF$UID + 1032 + + + CF$UID + 1033 + + + CF$UID + 1034 + + + CF$UID + 1035 + + + CF$UID + 1036 + + + CF$UID + 1037 + + + CF$UID + 1038 + + + CF$UID + 1039 + + + CF$UID + 1040 + + + CF$UID + 1041 + + + CF$UID + 1042 + + + CF$UID + 1043 + + + CF$UID + 1044 + + + CF$UID + 1045 + + + CF$UID + 1046 + + + CF$UID + 1047 + + + CF$UID + 1048 + + + CF$UID + 1049 + + + CF$UID + 1050 + + + CF$UID + 1051 + + + CF$UID + 1052 + + + CF$UID + 1053 + + + CF$UID + 1054 + + + CF$UID + 1055 + + + CF$UID + 1056 + + + CF$UID + 1057 + + + CF$UID + 1058 + + + CF$UID + 1059 + + + CF$UID + 1060 + + + CF$UID + 1061 + + + CF$UID + 1062 + + + CF$UID + 1063 + + + CF$UID + 1064 + + + CF$UID + 1065 + + + CF$UID + 1066 + + + CF$UID + 1067 + + + CF$UID + 1068 + + + CF$UID + 1069 + + + CF$UID + 1070 + + + CF$UID + 1071 + + + CF$UID + 1072 + + + CF$UID + 1073 + + + CF$UID + 1074 + + + CF$UID + 1075 + + + CF$UID + 1076 + + + CF$UID + 1077 + + + CF$UID + 1078 + + + CF$UID + 1079 + + + CF$UID + 1080 + + + CF$UID + 1081 + + + CF$UID + 1082 + + + CF$UID + 1083 + + + CF$UID + 1084 + + + CF$UID + 1085 + + + CF$UID + 1086 + + + CF$UID + 1087 + + + CF$UID + 1088 + + + CF$UID + 1089 + + + CF$UID + 1090 + + + CF$UID + 1091 + + + CF$UID + 1092 + + + + 728 + 235 + 540 + 130 + 111 + 729 + 589 + 181 + 587 + 320 + 242 + 730 + 650 + 173 + 351 + 574 + 624 + 328 + 361 + 23 + 731 + 251 + 145 + 39 + 164 + 677 + 618 + 732 + 669 + 678 + 316 + 231 + 737 + 679 + 245 + 733 + 460 + 236 + 535 + 680 + 24 + 615 + 738 + 643 + 558 + 322 + 462 + 681 + 739 + 714 + 324 + 571 + 682 + 142 + 243 + 610 + 625 + 136 + 740 + 325 + 510 + 573 + 602 + 683 + 645 + 326 + 37 + 741 + 248 + 150 + 575 + 511 + 172 + 684 + 662 + 744 + 576 + 29 + 685 + 232 + 631 + 742 + 21 + 577 + 686 + 240 + 613 + 745 + 584 + 318 + 753 + 238 + 594 + 541 + 689 + 663 + 743 + 56 + 176 + 623 + 595 + 321 + 690 + 319 + 146 + 746 + 156 + 598 + 226 + 616 + 696 + 197 + 246 + 329 + 152 + 747 + 103 + 591 + 700 + 153 + 752 + 748 + 250 + 149 + 590 + 195 + 704 + 464 + 175 + 749 + 198 + 705 + 160 + 475 + 583 + 750 + 365 + 178 + 666 + 673 + 143 + 617 + 348 + 706 + 237 + 228 + 751 + 239 + 597 + 144 + 225 + 716 + 619 + 638 + 754 + 543 + 614 + 715 + 599 + 642 + 647 + 665 + 756 + 641 + 157 + 476 + 717 + 582 + 247 + 317 + 327 + 639 + 755 + 461 + 2 + 229 + 718 + 477 + 179 + 759 + 249 + 719 + 498 + 572 + 362 + 601 + 349 + 196 + 644 + 720 + 504 + 713 + 169 + 163 + 131 + 139 + 509 + 721 + 668 + 5 + 667 + 323 + 241 + 723 + 57 + 646 + 158 + 659 + 640 + 758 + 724 + 711 + 622 + 180 + 674 + 171 + 129 + 106 + 660 + 626 + 725 + 350 + 58 + 19 + 600 + 588 + 586 + 620 + 358 + 637 + 230 + 726 + 463 + 661 + 757 + 244 + 672 + 727 + 134 + + $class + + CF$UID + 61 + + NS.objects + + + + $class + + CF$UID + 587 + + NS.objects + + + + $class + + CF$UID + 587 + + NS.objects + + + + $classes + + NSIBObjectData + NSObject + + $classname + NSIBObjectData + + + $top + + IB.objectdata + + CF$UID + 1 + + + $version + 100000 + + diff --git a/src/MAC/FileButtonController.h b/src/MAC/FileButtonController.h new file mode 100644 index 0000000..fcafc1e --- /dev/null +++ b/src/MAC/FileButtonController.h @@ -0,0 +1,26 @@ +// This file is hereby placed in the Public Domain -- Neil Stevens + +#import + +@interface FileButtonController : NSObject +{ + IBOutlet NSButton *button; + IBOutlet id field; + + NSArray *types; + bool allowMultiple; +} + +- (id)init; +- (void)dealloc; + +- (void)setTypes:(NSArray *)typeArray; +- (void)setAllowMultiple:(bool)allow; + +- (IBAction)buttonClicked:(id)sender; +- (void)panelEnded:(NSOpenPanel *)panel returnCode:(int)code contextInfo:(void *)info; + +- (id)field; +- (void)setEnabled:(BOOL)enabled; + +@end diff --git a/src/MAC/FileButtonController.m b/src/MAC/FileButtonController.m new file mode 100644 index 0000000..5dd3f1d --- /dev/null +++ b/src/MAC/FileButtonController.m @@ -0,0 +1,61 @@ +// This file is hereby placed in the Public Domain -- Neil Stevens + +#import "FileButtonController.h" + +@implementation FileButtonController + +- (id)init +{ + [super init]; + types = [[NSArray alloc] init]; + allowMultiple = false; + return self; +} + +- (void)dealloc +{ + [types release]; + [super dealloc]; +} + +- (void)setTypes:(NSArray *)typeArray +{ + [types release]; + types = [[NSArray alloc] initWithArray:typeArray]; +} + +- (void)setAllowMultiple:(bool)allow +{ + allowMultiple = allow; +} + +- (IBAction)buttonClicked:(id)sender +{ + NSOpenPanel *panel = [NSOpenPanel openPanel]; + [panel setAllowsMultipleSelection:allowMultiple]; + [panel setCanChooseFiles:true]; + [panel setCanChooseDirectories:false]; + [panel beginSheetForDirectory:nil file:nil types:types + modalForWindow:[NSApp mainWindow] modalDelegate:self + didEndSelector:@selector(panelEnded:returnCode:contextInfo:) + contextInfo:nil]; +} + +- (void)panelEnded:(NSOpenPanel *)panel returnCode:(int)code contextInfo:(void *)info +{ + if(code == NSCancelButton) return; + [field setStringValue:[[panel filenames] objectAtIndex:0]]; +} + +- (id)field +{ + return field; +} + +- (void)setEnabled:(BOOL)enabled +{ + [field setEnabled:enabled]; + [button setEnabled:enabled]; +} + +@end diff --git a/src/MAC/Info.plist b/src/MAC/Info.plist new file mode 100644 index 0000000..624c766 --- /dev/null +++ b/src/MAC/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + Launcher + CFBundleGetInfoString + %VERSION% + CFBundleIconFile + PrBoom.icns + CFBundleIdentifier + net.sourceforge.prboom-plus + CFBundleName + PrBoom + CFBundlePackageType + APPL + CFBundleVersion + %VERSION% + NSPrincipalClass + NSApplication + NSMainNibFile + MainMenu + NSAppleScriptEnabled + + OSAScriptingDefinition + PrBoom.sdef + + diff --git a/src/MAC/Launcher.icns b/src/MAC/Launcher.icns new file mode 100644 index 0000000..f11edaa Binary files /dev/null and b/src/MAC/Launcher.icns differ diff --git a/src/MAC/LauncherApp.h b/src/MAC/LauncherApp.h new file mode 100644 index 0000000..97d09e6 --- /dev/null +++ b/src/MAC/LauncherApp.h @@ -0,0 +1,90 @@ +// This file is hereby placed in the Public Domain -- Neil Stevens + +#import + +@interface LauncherApp : NSObject +{ + IBOutlet id window; + + // Game + IBOutlet id compatibilityLevelButton; + IBOutlet id gameButton; + IBOutlet id launchButton; + IBOutlet id gameMenu; + + // Options + IBOutlet id respawnMonstersButton; + IBOutlet id fastMonstersButton; + IBOutlet id noMonstersButton; + + IBOutlet id fullscreenButton; + IBOutlet id resolutionComboBox; + IBOutlet id graphicsModeComboBox; + + // Debug options + IBOutlet id disableGraphicsButton; + IBOutlet id disableJoystickButton; + IBOutlet id disableMouseButton; + IBOutlet id disableMusicButton; + IBOutlet id disableSoundButton; + IBOutlet id disableSoundEffectsButton; + IBOutlet id configFileButtonController; + + // Demo options + IBOutlet id noDemoButton; + IBOutlet id playDemoButton; + IBOutlet id fastDemoButton; + IBOutlet id timeDemoButton; + IBOutlet id demoMatrix; + + IBOutlet id ffToLevelField; + IBOutlet id demoFileButtonController; + + // Wad options + IBOutlet id wadViewController; + + // Drawers + IBOutlet id wadDrawer; + IBOutlet id demoDrawer; + IBOutlet id debugDrawer; + + // Console + IBOutlet id consoleController; +} + +- (NSString *)wadPath; +- (void)awakeFromNib; +- (void)windowWillClose:(NSNotification *)notification; + +- (IBAction)openWebsite:(id)sender; + +- (void)loadDefaults; +- (void)saveDefaults; + +- (NSString *)wadForIndex:(int)index; +- (NSString *)selectedWad; +- (void)updateGameWad; +- (void)watcher:(id)watcher receivedNotification:(NSString *)notification + forPath:(NSString *)path; + +// Game +- (void)tryToLaunch; +- (IBAction)startClicked:(id)sender; +- (void)taskEnded:(id)sender; +- (IBAction)gameButtonClicked:(id)sender; + +// Tools +- (IBAction)showGameFolderClicked:(id)sender; +- (IBAction)showConsoleClicked:(id)sender; + +// Options +- (IBAction)disableSoundClicked:(id)sender; + +// Demo options +- (IBAction)demoButtonClicked:(id)sender; + +@end + +@interface LaunchCommand : NSScriptCommand +- (id)performDefaultImplementation; +@end diff --git a/src/MAC/LauncherApp.m b/src/MAC/LauncherApp.m new file mode 100644 index 0000000..17eeb4d --- /dev/null +++ b/src/MAC/LauncherApp.m @@ -0,0 +1,383 @@ +// This file is hereby placed in the Public Domain -- Neil Stevens + +#import "ConsoleController.h" +#import "FileButtonController.h" +#import "LauncherApp.h" +#import "UKKQueue.h" +#import "WadViewController.h" + +#include + +static LauncherApp *LApp; + +@implementation LauncherApp + +- (NSString *)wadPath +{ + return [@"~/Library/Application Support/PrBoom-Plus" stringByExpandingTildeInPath]; +} + +- (void)awakeFromNib +{ + LApp = self; + + [[NSFileManager defaultManager] createDirectoryAtPath:[self wadPath] + attributes:nil]; + [[UKKQueue sharedQueue] setDelegate:self]; + [[UKKQueue sharedQueue] addPath:[self wadPath]]; + + [demoFileButtonController + setTypes:[NSArray arrayWithObjects:@"lmp", @"LMP", nil]]; + + [configFileButtonController + setTypes:[NSArray arrayWithObjects:@"cfg", @"CFG", nil]]; + + [self loadDefaults]; + + // Save Prefs on exit + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(saveDefaults) + name:NSApplicationWillTerminateNotification object:nil]; +} + +- (void)windowWillClose:(NSNotification *)notification +{ + [NSApp terminate:window]; +} + +- (void)openWebsite:(id)sender +{ + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://prboom.sourceforge.net/"]]; +} + +- (void)loadDefaults +{ + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + + [resolutionComboBox setObjectValue:[[resolutionComboBox dataSource] + comboBox:resolutionComboBox + objectValueForItemAtIndex:0]]; + [graphicsModeComboBox + setObjectValue:[graphicsModeComboBox itemObjectValueAtIndex:0]]; + + if([defaults boolForKey:@"Saved 2.4.6"]) + { + id res = [defaults objectForKey:@"Resolution"]; + if(NSNotFound != [[resolutionComboBox dataSource] + comboBox:resolutionComboBox + indexOfItemWithStringValue:res]) + [resolutionComboBox setObjectValue:res]; + + id mode = [defaults objectForKey:@"Graphics Mode"]; + if(NSNotFound != [graphicsModeComboBox + indexOfItemWithObjectValue:mode]) + [graphicsModeComboBox setObjectValue:[defaults objectForKey:@"Graphics Mode"]]; + } + + if([defaults boolForKey:@"Saved 2.4.5"]) + { + [window setFrameUsingName:@"Launcher"]; + if([[defaults objectForKey:@"Wad Drawer State"] boolValue]) + [wadDrawer open]; + if([[defaults objectForKey:@"Debug Drawer State"] boolValue]) + [debugDrawer open]; + if([[defaults objectForKey:@"Demo Drawer State"] boolValue]) + [demoDrawer open]; + if([[defaults objectForKey:@"Console State"] boolValue]) + { + [consoleController showWindow:self]; + [window makeKeyAndOrderFront:self]; + } + + [[configFileButtonController field] setObjectValue:[defaults objectForKey:@"Config File"]]; + } + + [[consoleController window] setFrameUsingName:@"Console"]; + + if([defaults boolForKey:@"Saved"] == true) + { + [gameButton setObjectValue:[defaults objectForKey:@"Game"]]; + [respawnMonstersButton setObjectValue:[defaults objectForKey:@"Respawn Monsters"]]; + [fastMonstersButton setObjectValue:[defaults objectForKey:@"Fast Monsters"]]; + [noMonstersButton setObjectValue:[defaults objectForKey:@"No Monsters"]]; + [fullscreenButton setObjectValue:[defaults objectForKey:@"Full Screen Graphics"]]; + [disableGraphicsButton setObjectValue:[defaults objectForKey:@"Disable Graphics"]]; + [disableJoystickButton setObjectValue:[defaults objectForKey:@"Disable Joystick"]]; + [disableMouseButton setObjectValue:[defaults objectForKey:@"Disable Mouse"]]; + [disableMusicButton setObjectValue:[defaults objectForKey:@"Disable Music"]]; + [disableSoundButton setObjectValue:[defaults objectForKey:@"Disable Sound"]]; + [disableSoundEffectsButton setObjectValue:[defaults objectForKey:@"Disable Sound Effects"]]; + [wadViewController setWads:[defaults stringArrayForKey:@"Wads"]]; + + // Store the compat level in terms of the Prboom values, rather than + // our internal indices. That means we have to add one when we read + // the settings, and subtract one when we save it + long compatIndex = [[defaults objectForKey:@"Compatibility Level"] + longValue] + 1; + [compatibilityLevelButton setObjectValue:[NSNumber + numberWithLong:compatIndex]]; + } + else + { + [compatibilityLevelButton setObjectValue:[NSNumber numberWithLong:0]]; + } + + [self disableSoundClicked:disableSoundButton]; + [self demoButtonClicked:demoMatrix]; + [self updateGameWad]; +} + +- (void)saveDefaults +{ + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + + [defaults setBool:true forKey:@"Saved"]; + [defaults setBool:true forKey:@"Saved 2.4.5"]; + [defaults setBool:true forKey:@"Saved 2.4.6"]; + + [defaults setObject:[resolutionComboBox objectValue] forKey:@"Resolution"]; + [defaults setObject:[graphicsModeComboBox objectValue] forKey:@"Graphics Mode"]; + + [window saveFrameUsingName:@"Launcher"]; + [[consoleController window] saveFrameUsingName:@"Console"]; + + [defaults setObject:[NSNumber numberWithBool:[wadDrawer state]] forKey:@"Wad Drawer State"]; + [defaults setObject:[NSNumber numberWithBool:[debugDrawer state]] forKey:@"Debug Drawer State"]; + [defaults setObject:[NSNumber numberWithBool:[demoDrawer state]] forKey:@"Demo Drawer State"]; + + [defaults setObject:[NSNumber numberWithBool:[[consoleController window] isVisible]] forKey:@"Console State"]; + + [defaults setObject:[[configFileButtonController field] objectValue] forKey:@"Config File"]; + + [defaults setObject:[gameButton objectValue] forKey:@"Game"]; + [defaults setObject:[respawnMonstersButton objectValue] forKey:@"Respawn Monsters"]; + [defaults setObject:[fastMonstersButton objectValue] forKey:@"Fast Monsters"]; + [defaults setObject:[noMonstersButton objectValue] forKey:@"No Monsters"]; + [defaults setObject:[fullscreenButton objectValue] forKey:@"Full Screen Graphics"]; + [defaults setObject:[disableGraphicsButton objectValue] forKey:@"Disable Graphics"]; + [defaults setObject:[disableJoystickButton objectValue] forKey:@"Disable Joystick"]; + [defaults setObject:[disableMouseButton objectValue] forKey:@"Disable Mouse"]; + [defaults setObject:[disableMusicButton objectValue] forKey:@"Disable Music"]; + [defaults setObject:[disableSoundButton objectValue] forKey:@"Disable Sound"]; + [defaults setObject:[disableSoundEffectsButton objectValue] forKey:@"Disable Sound Effects"]; + [defaults setObject:[wadViewController wads] forKey:@"Wads"]; + + // Store the compat level in terms of the Prboom values, rather than + // our internal indices. That means we have to add one when we read + // the settings, and subtract one when we save it + long compatLevel = [[compatibilityLevelButton objectValue] longValue] - 1; + [defaults setObject:[NSNumber numberWithLong:compatLevel] forKey:@"Compatibility Level"]; + + [defaults synchronize]; +} + +- (NSString *)wadForIndex:(int)index +{ + if(index == 0) + return @"doom.wad"; + else if(index == 1) + return @"doomu.wad"; + else if(index == 2) + return @"doom2.wad"; + else if(index == 3) + return @"tnt.wad"; + else if(index == 4) + return @"plutonia.wad"; + else if(index == 5) + return @"freedoom.wad"; + else + return nil; +} + +- (NSString *)selectedWad +{ + return [self wadForIndex:[[gameButton objectValue] longValue]]; +} + +- (void)updateGameWad +{ + int i; + for(i = 0; i < [gameMenu numberOfItems]; ++i) + { + NSString *path = [[[self wadPath] stringByAppendingString:@"/"] + stringByAppendingString:[self wadForIndex:i]]; + bool exists = [[NSFileManager defaultManager] fileExistsAtPath:path]; + [[gameMenu itemAtIndex:i] setEnabled:exists]; + if([[gameButton objectValue] longValue] == i) + [launchButton setEnabled:exists]; + } +} + +- (void)watcher:(id)watcher receivedNotification:(NSString *)notification + forPath:(NSString *)path +{ + [self updateGameWad]; +} + +- (void)tryToLaunch +{ + if([launchButton isEnabled]) + [self startClicked:self]; +} + +- (IBAction)startClicked:(id)sender +{ + [self saveDefaults]; + + NSString *path = [[NSBundle mainBundle] pathForAuxiliaryExecutable:@"PrBoom-Plus"]; + NSMutableArray *args = [NSMutableArray arrayWithCapacity:10]; + + // redirect all output to stdout + [args insertObject:@"-cout" atIndex:[args count]]; + [args insertObject:@"ICWEFDA" atIndex:[args count]]; + + [args insertObject:@"-cerr" atIndex:[args count]]; + + // Game + [args insertObject:@"-iwad" atIndex:[args count]]; + [args insertObject:[self selectedWad] atIndex:[args count]]; + + // Compat + long compatLevel = [[compatibilityLevelButton objectValue] longValue] - 1; + [args insertObject:@"-complevel" atIndex:[args count]]; + [args insertObject:[[NSNumber numberWithLong:compatLevel] stringValue] + atIndex:[args count]]; + + // Options + if([fastMonstersButton state] == NSOnState) + [args insertObject:@"-fast" atIndex:[args count]]; + if([noMonstersButton state] == NSOnState) + [args insertObject:@"-nomonsters" atIndex:[args count]]; + if([respawnMonstersButton state] == NSOnState) + [args insertObject:@"-respawn" atIndex:[args count]]; + + if([fullscreenButton state] == NSOnState) + [args insertObject:@"-nowindow" atIndex:[args count]]; + else + [args insertObject:@"-window" atIndex:[args count]]; + + [args insertObject:@"-geom" atIndex:[args count]]; + [args insertObject:[resolutionComboBox objectValue] atIndex:[args count]]; + + if([[graphicsModeComboBox objectValue] hasPrefix:@"OpenGL"]) + { + [args insertObject:@"-vidmode" atIndex:[args count]]; + [args insertObject:@"gl" atIndex:[args count]]; + } + else if([[graphicsModeComboBox objectValue] hasPrefix:@"8"]) + { + [args insertObject:@"-vidmode" atIndex:[args count]]; + [args insertObject:@"8" atIndex:[args count]]; + } + + // Debug options + if([disableGraphicsButton state] == NSOnState) + [args insertObject:@"-nodraw" atIndex:[args count]]; + if([disableJoystickButton state] == NSOnState) + [args insertObject:@"-nojoy" atIndex:[args count]]; + if([disableMouseButton state] == NSOnState) + [args insertObject:@"-nomouse" atIndex:[args count]]; + if([disableSoundButton state] == NSOnState) + { + [args insertObject:@"-nosound" atIndex:[args count]]; + } + else + { + if([disableMusicButton state] == NSOnState) + [args insertObject:@"-nomusic" atIndex:[args count]]; + if([disableSoundEffectsButton state] == NSOnState) + [args insertObject:@"-nosfx" atIndex:[args count]]; + } + if([[[configFileButtonController field] stringValue] length] > 0) + { + [args insertObject:@"-config" atIndex:[args count]]; + [args insertObject:[[configFileButtonController field] stringValue] atIndex:[args count]]; + } + + // Extra wads + [args insertObject:@"-file" atIndex:[args count]]; + int i; + NSArray *wads = [wadViewController wads]; + for(i = 0; i < [wads count]; ++i) + { + NSString *path = [wads objectAtIndex:i]; + if([[path pathExtension] caseInsensitiveCompare:@"wad"] == NSOrderedSame) + [args insertObject:[wads objectAtIndex:i] atIndex:[args count]]; + } + + // Dehacked + [args insertObject:@"-deh" atIndex:[args count]]; + for(i = 0; i < [wads count]; ++i) + { + NSString *path = [wads objectAtIndex:i]; + if([[path pathExtension] caseInsensitiveCompare:@"deh"] == NSOrderedSame) + [args insertObject:[wads objectAtIndex:i] atIndex:[args count]]; + } + + // Demo + if([demoMatrix selectedCell] != noDemoButton) + { + if([demoMatrix selectedCell] == playDemoButton) + [args insertObject:@"-playdemo" atIndex:[args count]]; + else if([demoMatrix selectedCell] == timeDemoButton) + [args insertObject:@"-timedemo" atIndex:[args count]]; + else if([demoMatrix selectedCell] == fastDemoButton) + [args insertObject:@"-fastdemo" atIndex:[args count]]; + + [args insertObject:[[demoFileButtonController field] stringValue] atIndex:[args count]]; + + if([[ffToLevelField stringValue] length] > 0) + { + [args insertObject:@"-ffmap" atIndex:[args count]]; + [args insertObject:[ffToLevelField stringValue] atIndex:[args count]]; + } + } + + [launchButton setEnabled:false]; + [consoleController launch:path args:args delegate:self]; +} + +- (void)taskEnded:(id)sender +{ + [launchButton setEnabled:true]; +} + +- (IBAction)gameButtonClicked:(id)sender +{ + [self updateGameWad]; +} + +- (IBAction)showGameFolderClicked:(id)sender +{ + [[NSWorkspace sharedWorkspace] openFile:[self wadPath] withApplication:@"Finder"]; +} + +- (IBAction)showConsoleClicked:(id)sender +{ + [consoleController showWindow:sender]; +} + +- (IBAction)disableSoundClicked:(id)sender +{ + bool state = [disableSoundButton state] != NSOnState; + [disableSoundEffectsButton setEnabled:state]; + [disableMusicButton setEnabled:state]; +} + +- (IBAction)demoButtonClicked:(id)sender +{ + bool enabled = [demoMatrix selectedCell] != noDemoButton; + [demoFileButtonController setEnabled:enabled]; + [ffToLevelField setEnabled:enabled]; +} + +@end + +@implementation LaunchCommand + +- (id)performDefaultImplementation +{ + [LApp tryToLaunch]; +} + +@end diff --git a/src/MAC/LauncherMain.m b/src/MAC/LauncherMain.m new file mode 100644 index 0000000..2837df7 --- /dev/null +++ b/src/MAC/LauncherMain.m @@ -0,0 +1,8 @@ +// This file is hereby placed in the Public Domain -- Neil Stevens + +#import + +int main(int argc, char *argv[]) +{ + return NSApplicationMain(argc, (const char **) argv); +} diff --git a/src/MAC/PrBoom.icns b/src/MAC/PrBoom.icns new file mode 100644 index 0000000..3b703d2 Binary files /dev/null and b/src/MAC/PrBoom.icns differ diff --git a/src/MAC/PrBoom.sdef b/src/MAC/PrBoom.sdef new file mode 100644 index 0000000..3f69b00 --- /dev/null +++ b/src/MAC/PrBoom.sdef @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/MAC/ResolutionDataSource.h b/src/MAC/ResolutionDataSource.h new file mode 100644 index 0000000..af5b2e0 --- /dev/null +++ b/src/MAC/ResolutionDataSource.h @@ -0,0 +1,36 @@ +// Copyright (C) 2006 Neil Stevens +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name(s) of the author(s) shall not be +// used in advertising or otherwise to promote the sale, use or other dealings +// in this Software without prior written authorization from the author(s). + +#import + +@interface ResolutionDataSource : NSObject +{ +} + ++ (NSArray *)resolutions; + +- (id)comboBox:(NSComboBox *)box objectValueForItemAtIndex:(int)i; +- (int)comboBox:(NSComboBox *)box indexOfItemWithStringValue:(NSString *)string; +- (int)numberOfItemsInComboBox:(NSComboBox *)box; + +@end diff --git a/src/MAC/ResolutionDataSource.m b/src/MAC/ResolutionDataSource.m new file mode 100644 index 0000000..8f484ba --- /dev/null +++ b/src/MAC/ResolutionDataSource.m @@ -0,0 +1,140 @@ +// Copyright (C) 2006 Neil Stevens +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name(s) of the author(s) shall not be +// used in advertising or otherwise to promote the sale, use or other dealings +// in this Software without prior written authorization from the author(s). + +#import +#import "ResolutionDataSource.h" + +@implementation ResolutionDataSource + +static int resolutionSort(id first, id second, void *context) +{ + NSArray *firstComponents = [first componentsSeparatedByString:@"x"]; + NSArray *secondComponents = [second componentsSeparatedByString:@"x"]; + int firstW = [[firstComponents objectAtIndex:0] intValue]; + int firstH = [[firstComponents objectAtIndex:1] intValue]; + int secondW = [[secondComponents objectAtIndex:0] intValue]; + int secondH = [[secondComponents objectAtIndex:1] intValue]; + + if(firstW < secondW) + return NSOrderedAscending; + else if(firstW > secondW) + return NSOrderedDescending; + else if(firstH < secondH) + return NSOrderedAscending; + else if(firstH > secondH) + return NSOrderedDescending; + else + return NSOrderedSame; +} + +static void addResolution(NSMutableArray *array, int w, int h) +{ + NSString *str = [NSString stringWithFormat:@"%ix%i", w, h]; + if(![array containsObject:str]) + [array addObject:str]; +} + ++ (NSArray *)resolutions +{ + NSMutableArray *retval = [[[NSMutableArray alloc] init] autorelease]; + NSArray *modes = (NSArray *)CGDisplayAvailableModes(CGMainDisplayID()); + int i; + for(i = 0; i < [modes count]; ++i) + { + NSDictionary *mode = [modes objectAtIndex:i]; + int w = [[mode valueForKey:(NSString *)kCGDisplayWidth] intValue]; + int h = [[mode valueForKey:(NSString *)kCGDisplayHeight] intValue]; + float ratio = (float) w / (float) h; + float r1 = 4.0 / 3.0; + float r2 = 8.0 / 5.0; + + // Skip unsafe resolutions + if(![mode objectForKey:(NSString *)kCGDisplayModeIsSafeForHardware]) + continue; + + // Bounds of supported PrBoom resolutions + if(w > 2048) + w = 2048; + if(h > 1536) + h = 1536; + if(w < 320) + continue; + if(h < 200) + continue; + + if(h * 4 / 3 == w) + { + // 4:3 screen (640x480) + // Use as is, and offer 8:5 + addResolution(retval, w, h); + addResolution(retval, w, w * 5 / 8); + } + else if(ratio > r1 && ratio < r2) + { + // Wide screen less than 8:5 (700x500) + // Offer nearest 4:3 and 8:5 + addResolution(retval, w, w * 5 / 8); + addResolution(retval, h * 4 / 3, h); + } + else if(h * 8 / 5 == w) + { + // 8:5 screen (320x200) + // Use as is + addResolution(retval, w, h); + } + else if(ratio < r1) + { + // Narrow screen (1280x1024) + // Offer 4:3 + addResolution(retval, w, w * 3 / 4); + } + else + { + // Wide wide screen (2000x1000) + // Offer 8:5 + addResolution(retval, h * 8 / 5, h); + } + } + [retval sortUsingFunction:resolutionSort context:nil]; + return retval; +} + +- (id)comboBox:(NSComboBox *)box objectValueForItemAtIndex:(int)i +{ + NSArray *modes = [ResolutionDataSource resolutions]; + return [modes objectAtIndex:i]; +} + +- (int)comboBox:(NSComboBox *)box indexOfItemWithStringValue:(NSString *)string +{ + NSArray *modes = [ResolutionDataSource resolutions]; + return [modes indexOfObject:string]; +} + +- (int)numberOfItemsInComboBox:(NSComboBox *)box +{ + NSArray *modes = [ResolutionDataSource resolutions]; + return [modes count]; +} + +@end diff --git a/src/MAC/TODO b/src/MAC/TODO new file mode 100644 index 0000000..9dd4ccf --- /dev/null +++ b/src/MAC/TODO @@ -0,0 +1,6 @@ +Launcher features: + + - Demo Recording + - Give option to load arbitrary IWAD + - Show GUI version of common errors + - Support network play diff --git a/src/MAC/UKFileWatcher.h b/src/MAC/UKFileWatcher.h new file mode 100644 index 0000000..59ea480 --- /dev/null +++ b/src/MAC/UKFileWatcher.h @@ -0,0 +1,57 @@ +/* ============================================================================= + FILE: UKFileWatcher.h + PROJECT: Filie + + COPYRIGHT: (c) 2005 M. Uli Kusterer, all rights reserved. + + AUTHORS: M. Uli Kusterer - UK + + LICENSES: GPL, Modified BSD + + REVISIONS: + 2005-02-25 UK Created. + ========================================================================== */ + +/* + This is a protocol that file change notification classes should adopt. + That way, no matter whether you use Carbon's FNNotify/FNSubscribe, BSD's + kqueue or whatever, the object being notified can react to change + notifications the same way, and you can easily swap one out for the other + to cater to different OS versions, target volumes etc. +*/ + +// ----------------------------------------------------------------------------- +// Protocol: +// ----------------------------------------------------------------------------- + +@protocol UKFileWatcher + +-(void) addPath: (NSString*)path; +-(void) removePath: (NSString*)path; + +-(id) delegate; +-(void) setDelegate: (id)newDelegate; + +@end + +// ----------------------------------------------------------------------------- +// Methods delegates need to provide: +// ----------------------------------------------------------------------------- + +@interface NSObject (UKFileWatcherDelegate) + +-(void) watcher: (id)kq receivedNotification: (NSString*)nm forPath: (NSString*)fpath; + +@end + + +// Notifications this sends: +// (object is the file path registered with, and these are sent via the workspace notification center) +#define UKFileWatcherRenameNotification @"UKKQueueFileRenamedNotification" +#define UKFileWatcherWriteNotification @"UKKQueueFileWrittenToNotification" +#define UKFileWatcherDeleteNotification @"UKKQueueFileDeletedNotification" +#define UKFileWatcherAttributeChangeNotification @"UKKQueueFileAttributesChangedNotification" +#define UKFileWatcherSizeIncreaseNotification @"UKKQueueFileSizeIncreasedNotification" +#define UKFileWatcherLinkCountChangeNotification @"UKKQueueFileLinkCountChangedNotification" +#define UKFileWatcherAccessRevocationNotification @"UKKQueueFileAccessRevocationNotification" + diff --git a/src/MAC/UKKQueue.h b/src/MAC/UKKQueue.h new file mode 100644 index 0000000..5ec7015 --- /dev/null +++ b/src/MAC/UKKQueue.h @@ -0,0 +1,100 @@ +/* ============================================================================= + FILE: UKKQueue.h + PROJECT: Filie + + COPYRIGHT: (c) 2003 M. Uli Kusterer, all rights reserved. + + AUTHORS: M. Uli Kusterer - UK + + LICENSES: GPL, Modified BSD + + REVISIONS: + 2003-12-21 UK Created. + ========================================================================== */ + +// ----------------------------------------------------------------------------- +// Headers: +// ----------------------------------------------------------------------------- + +#import +#include +#include +#import "UKFileWatcher.h" + + +// ----------------------------------------------------------------------------- +// Constants: +// ----------------------------------------------------------------------------- + +#ifndef UKKQUEUE_BACKWARDS_COMPATIBLE +#define UKKQUEUE_BACKWARDS_COMPATIBLE 1 // 1 to send old-style kqueue:receivedNotification:forFile: messages to objects that accept them. +#endif + +// Flags for notifyingAbout: +#define UKKQueueNotifyAboutRename NOTE_RENAME // Item was renamed. +#define UKKQueueNotifyAboutWrite NOTE_WRITE // Item contents changed (also folder contents changed). +#define UKKQueueNotifyAboutDelete NOTE_DELETE // item was removed. +#define UKKQueueNotifyAboutAttributeChange NOTE_ATTRIB // Item attributes changed. +#define UKKQueueNotifyAboutSizeIncrease NOTE_EXTEND // Item size increased. +#define UKKQueueNotifyAboutLinkCountChanged NOTE_LINK // Item's link count changed. +#define UKKQueueNotifyAboutAccessRevocation NOTE_REVOKE // Access to item was revoked. + +// Notifications this sends: +// (see UKFileWatcher) +// Old names: *deprecated* +#define UKKQueueFileRenamedNotification UKFileWatcherRenameNotification +#define UKKQueueFileWrittenToNotification UKFileWatcherWriteNotification +#define UKKQueueFileDeletedNotification UKFileWatcherDeleteNotification +#define UKKQueueFileAttributesChangedNotification UKFileWatcherAttributeChangeNotification +#define UKKQueueFileSizeIncreasedNotification UKFileWatcherSizeIncreaseNotification +#define UKKQueueFileLinkCountChangedNotification UKFileWatcherLinkCountChangeNotification +#define UKKQueueFileAccessRevocationNotification UKFileWatcherAccessRevocationNotification + + +// ----------------------------------------------------------------------------- +// UKKQueue: +// ----------------------------------------------------------------------------- + +@interface UKKQueue : NSObject +{ + int queueFD; // The actual queue ID. + NSMutableArray* watchedPaths; // List of NSStrings containing the paths we're watching. + NSMutableArray* watchedFDs; // List of NSNumbers containing the file descriptors we're watching. + id delegate; // Gets messages about changes instead of notification center, if specified. + id delegateProxy; // Proxy object to which we send messages so they reach delegate on the main thread. + BOOL alwaysNotify; // Send notifications even if we have a delegate? Defaults to NO. + BOOL keepThreadRunning; // Termination criterion of our thread. +} + ++(UKKQueue*) sharedQueue; // Returns a singleton, a shared kqueue object Handy if you're subscribing to the notifications. Use this, or just create separate objects using alloc/init. Whatever floats your boat. + +-(int) queueFD; // I know you unix geeks want this... + +// High-level file watching: (use UKFileWatcher protocol methods instead, where possible!) +-(void) addPathToQueue: (NSString*)path; +-(void) addPathToQueue: (NSString*)path notifyingAbout: (u_int)fflags; +-(void) removePathFromQueue: (NSString*)path; + +-(id) delegate; +-(void) setDelegate: (id)newDelegate; + +-(BOOL) alwaysNotify; +-(void) setAlwaysNotify: (BOOL)n; + +// private: +-(void) watcherThread: (id)sender; +-(void) postNotification: (NSString*)nm forFile: (NSString*)fp; // Message-posting bottleneck. + +@end + + +// ----------------------------------------------------------------------------- +// Methods delegates need to provide: +// * DEPRECATED * use UKFileWatcher delegate methods instead! +// ----------------------------------------------------------------------------- + +@interface NSObject (UKKQueueDelegate) + +-(void) kqueue: (UKKQueue*)kq receivedNotification: (NSString*)nm forFile: (NSString*)fpath; + +@end diff --git a/src/MAC/UKKQueue.m b/src/MAC/UKKQueue.m new file mode 100644 index 0000000..f07e2bb --- /dev/null +++ b/src/MAC/UKKQueue.m @@ -0,0 +1,464 @@ +/* ============================================================================= + FILE: UKKQueue.m + PROJECT: Filie + + COPYRIGHT: (c) 2003 M. Uli Kusterer, all rights reserved. + + AUTHORS: M. Uli Kusterer - UK + + LICENSES: GPL, Modified BSD + + REVISIONS: + 2004-12-28 UK Several threading fixes. + 2003-12-21 UK Created. + ========================================================================== */ + +// ----------------------------------------------------------------------------- +// Headers: +// ----------------------------------------------------------------------------- + +#import +#import "UKKQueue.h" +#import "UKMainThreadProxy.h" +#import +#import + + +// ----------------------------------------------------------------------------- +// Macros: +// ----------------------------------------------------------------------------- + +// @synchronized isn't available prior to 10.3, so we use a typedef so +// this class is thread-safe on Panther but still compiles on older OSs. + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3 +#define AT_SYNCHRONIZED(n) @synchronized(n) +#else +#define AT_SYNCHRONIZED(n) +#endif + + +// ----------------------------------------------------------------------------- +// Globals: +// ----------------------------------------------------------------------------- + +static UKKQueue * gUKKQueueSharedQueueSingleton = nil; + + +@implementation UKKQueue + +// ----------------------------------------------------------------------------- +// sharedQueue: +// Returns a singleton queue object. In many apps (especially those that +// subscribe to the notifications) there will only be one kqueue instance, +// and in that case you can use this. +// +// For all other cases, feel free to create additional instances to use +// independently. +// +// REVISIONS: +// 2005-07-02 UK Created. +// ----------------------------------------------------------------------------- + ++(UKKQueue*) sharedQueue +{ + AT_SYNCHRONIZED( self ) + { + if( !gUKKQueueSharedQueueSingleton ) + gUKKQueueSharedQueueSingleton = [[UKKQueue alloc] init]; // This is a singleton, and thus an intentional "leak". + } + + return gUKKQueueSharedQueueSingleton; +} + + +// ----------------------------------------------------------------------------- +// * CONSTRUCTOR: +// Creates a new KQueue and starts that thread we use for our +// notifications. +// +// REVISIONS: +// 2004-11-12 UK Doesn't pass self as parameter to watcherThread anymore, +// because detachNewThreadSelector retains target and args, +// which would cause us to never be released. +// 2004-03-13 UK Documented. +// ----------------------------------------------------------------------------- + +-(id) init +{ + self = [super init]; + if( self ) + { + queueFD = kqueue(); + if( queueFD == -1 ) + { + [self release]; + return nil; + } + + watchedPaths = [[NSMutableArray alloc] init]; + watchedFDs = [[NSMutableArray alloc] init]; + + // Start new thread that fetches and processes our events: + keepThreadRunning = YES; + [NSThread detachNewThreadSelector:@selector(watcherThread:) toTarget:self withObject:nil]; + } + + return self; +} + + +// ----------------------------------------------------------------------------- +// release: +// Since NSThread retains its target, we need this method to terminate the +// thread when we reach a retain-count of two. The thread is terminated by +// setting keepThreadRunning to NO. +// +// REVISIONS: +// 2004-11-12 UK Created. +// ----------------------------------------------------------------------------- + +-(oneway void) release +{ + AT_SYNCHRONIZED(self) + { + //NSLog(@"%@ (%d)", self, [self retainCount]); + if( [self retainCount] == 2 && keepThreadRunning ) + keepThreadRunning = NO; + } + + [super release]; +} + +// ----------------------------------------------------------------------------- +// * DESTRUCTOR: +// Releases the kqueue again. +// +// REVISIONS: +// 2004-03-13 UK Documented. +// ----------------------------------------------------------------------------- + +-(void) dealloc +{ + delegate = nil; + [delegateProxy release]; + + if( keepThreadRunning ) + keepThreadRunning = NO; + + // Close all our file descriptors so the files can be deleted: + NSEnumerator* enny = [watchedFDs objectEnumerator]; + NSNumber* fdNum; + while( (fdNum = [enny nextObject]) ) + { + if( close( [fdNum intValue] ) == -1 ) + NSLog(@"dealloc: Couldn't close file descriptor (%d)", errno); + } + + [watchedPaths release]; + watchedPaths = nil; + [watchedFDs release]; + watchedFDs = nil; + + [super dealloc]; + + //NSLog(@"kqueue released."); +} + + +// ----------------------------------------------------------------------------- +// queueFD: +// Returns a Unix file descriptor for the KQueue this uses. The descriptor +// is owned by this object. Do not close it! +// +// REVISIONS: +// 2004-03-13 UK Documented. +// ----------------------------------------------------------------------------- + +-(int) queueFD +{ + return queueFD; +} + + +// ----------------------------------------------------------------------------- +// addPathToQueue: +// Tell this queue to listen for all interesting notifications sent for +// the object at the specified path. If you want more control, use the +// addPathToQueue:notifyingAbout: variant instead. +// +// REVISIONS: +// 2004-03-13 UK Documented. +// ----------------------------------------------------------------------------- + +-(void) addPathToQueue: (NSString*)path +{ + [self addPathToQueue: path notifyingAbout: UKKQueueNotifyAboutRename + | UKKQueueNotifyAboutWrite + | UKKQueueNotifyAboutDelete + | UKKQueueNotifyAboutAttributeChange]; +} + + +-(void) addPath: (NSString*)path +{ + [self addPathToQueue: path notifyingAbout: UKKQueueNotifyAboutRename + | UKKQueueNotifyAboutWrite + | UKKQueueNotifyAboutDelete + | UKKQueueNotifyAboutAttributeChange]; +} + + +// ----------------------------------------------------------------------------- +// addPathToQueue:notfyingAbout: +// Tell this queue to listen for the specified notifications sent for +// the object at the specified path. +// +// REVISIONS: +// 2005-06-29 UK Files are now opened using O_EVTONLY instead of O_RDONLY +// which allows ejecting or deleting watched files/folders. +// Thanks to Phil Hargett for finding this flag in the docs. +// 2004-03-13 UK Documented. +// ----------------------------------------------------------------------------- + +-(void) addPathToQueue: (NSString*)path notifyingAbout: (u_int)fflags +{ + struct timespec nullts = { 0, 0 }; + struct kevent ev; + int fd = open( [path fileSystemRepresentation], O_EVTONLY, 0 ); + + if( fd >= 0 ) + { + EV_SET( &ev, fd, EVFILT_VNODE, + EV_ADD | EV_ENABLE | EV_CLEAR, + fflags, 0, (void*)path ); + + AT_SYNCHRONIZED( self ) + { + [watchedPaths addObject: path]; + [watchedFDs addObject: [NSNumber numberWithInt: fd]]; + kevent( queueFD, &ev, 1, NULL, 0, &nullts ); + } + } +} + + +-(void) removePath: (NSString*)path +{ + [self removePathFromQueue: path]; +} + + +// ----------------------------------------------------------------------------- +// removePathFromQueue: +// Stop listening for changes to the specified path. This removes all +// notifications. Use this to balance both addPathToQueue:notfyingAbout: +// as well as addPathToQueue:. +// +// REVISIONS: +// 2004-03-13 UK Documented. +// ----------------------------------------------------------------------------- + +-(void) removePathFromQueue: (NSString*)path +{ + int index = 0; + int fd = -1; + + AT_SYNCHRONIZED( self ) + { + index = [watchedPaths indexOfObject: path]; + + if( index == NSNotFound ) + return; + + fd = [[watchedFDs objectAtIndex: index] intValue]; + + [watchedFDs removeObjectAtIndex: index]; + [watchedPaths removeObjectAtIndex: index]; + } + + if( close( fd ) == -1 ) + NSLog(@"removePathFromQueue: Couldn't close file descriptor (%d)", errno); +} + + +// ----------------------------------------------------------------------------- +// removeAllPathsFromQueue: +// Stop listening for changes to all paths. This removes all +// notifications. +// +// REVISIONS: +// 2004-12-28 UK Added as suggested by bbum. +// ----------------------------------------------------------------------------- + +-(void) removeAllPathsFromQueue; +{ + AT_SYNCHRONIZED( self ) + { + NSEnumerator * fdEnumerator = [watchedFDs objectEnumerator]; + NSNumber * anFD; + + while( (anFD = [fdEnumerator nextObject]) != nil ) + close( [anFD intValue] ); + + [watchedFDs removeAllObjects]; + [watchedPaths removeAllObjects]; + } +} + + +// ----------------------------------------------------------------------------- +// watcherThread: +// This method is called by our NSThread to loop and poll for any file +// changes that our kqueue wants to tell us about. This sends separate +// notifications for the different kinds of changes that can happen. +// All messages are sent via the postNotification:forFile: main bottleneck. +// +// This also calls sharedWorkspace's noteFileSystemChanged. +// +// To terminate this method (and its thread), set keepThreadRunning to NO. +// +// REVISIONS: +// 2005-08-27 UK Changed to use keepThreadRunning instead of kqueueFD +// being -1 as termination criterion, and to close the +// queue in this thread so the main thread isn't blocked. +// 2004-11-12 UK Fixed docs to include termination criterion, added +// timeout to make sure the bugger gets disposed. +// 2004-03-13 UK Documented. +// ----------------------------------------------------------------------------- + +-(void) watcherThread: (id)sender +{ + int n; + struct kevent ev; + struct timespec timeout = { 5, 0 }; // 5 seconds timeout. + int theFD = queueFD; // So we don't have to risk accessing iVars when the thread is terminated. + + while( keepThreadRunning ) + { + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + NS_DURING + n = kevent( queueFD, NULL, 0, &ev, 1, &timeout ); + if( n > 0 ) + { + if( ev.filter == EVFILT_VNODE ) + { + if( ev.fflags ) + { + NSString* fpath = [[(NSString *)ev.udata retain] autorelease]; // In case one of the notified folks removes the path. + //NSLog(@"UKKQueue: Detected file change: %@", fpath); + [[NSWorkspace sharedWorkspace] noteFileSystemChanged: fpath]; + + if( (ev.fflags & NOTE_RENAME) == NOTE_RENAME ) + [self postNotification: UKKQueueFileRenamedNotification forFile: fpath]; + if( (ev.fflags & NOTE_WRITE) == NOTE_WRITE ) + [self postNotification: UKKQueueFileWrittenToNotification forFile: fpath]; + if( (ev.fflags & NOTE_DELETE) == NOTE_DELETE ) + [self postNotification: UKKQueueFileDeletedNotification forFile: fpath]; + if( (ev.fflags & NOTE_ATTRIB) == NOTE_ATTRIB ) + [self postNotification: UKKQueueFileAttributesChangedNotification forFile: fpath]; + if( (ev.fflags & NOTE_EXTEND) == NOTE_EXTEND ) + [self postNotification: UKKQueueFileSizeIncreasedNotification forFile: fpath]; + if( (ev.fflags & NOTE_LINK) == NOTE_LINK ) + [self postNotification: UKKQueueFileLinkCountChangedNotification forFile: fpath]; + if( (ev.fflags & NOTE_REVOKE) == NOTE_REVOKE ) + [self postNotification: UKKQueueFileAccessRevocationNotification forFile: fpath]; + } + } + } + NS_HANDLER + NSLog(@"Error in UKKQueue watcherThread: %@",localException); + NS_ENDHANDLER + + [pool release]; + } + + // Close our kqueue's file descriptor: + if( close( theFD ) == -1 ) + NSLog(@"release: Couldn't close main kqueue (%d)", errno); + + //NSLog(@"exiting kqueue watcher thread."); +} + + +// ----------------------------------------------------------------------------- +// postNotification:forFile: +// This is the main bottleneck for posting notifications. If you don't want +// the notifications to go through NSWorkspace, override this method and +// send them elsewhere. +// +// REVISIONS: +// 2004-02-27 UK Changed this to send new notification, and the old one +// only to objects that respond to it. The old category on +// NSObject could cause problems with the proxy itself. +// 2004-10-31 UK Helloween fun: Make this use a mainThreadProxy and +// allow sending the notification even if we have a +// delegate. +// 2004-03-13 UK Documented. +// ----------------------------------------------------------------------------- + +-(void) postNotification: (NSString*)nm forFile: (NSString*)fp +{ + if( delegateProxy ) + { + #if UKKQUEUE_BACKWARDS_COMPATIBLE + if( ![delegateProxy respondsToSelector: @selector(watcher:receivedNotification:forPath:)] ) + [delegateProxy kqueue: self receivedNotification: nm forFile: fp]; + else + #endif + [delegateProxy watcher: self receivedNotification: nm forPath: fp]; + } + + if( !delegateProxy || alwaysNotify ) + [[[NSWorkspace sharedWorkspace] notificationCenter] postNotificationName: nm object: fp]; + //NSLog(@"Notification: %@ (%@)", nm, fp); +} + +-(id) delegate +{ + return delegate; +} + +-(void) setDelegate: (id)newDelegate +{ + id oldProxy = delegateProxy; + delegate = newDelegate; + delegateProxy = [delegate copyMainThreadProxy]; + [oldProxy release]; +} + +// ----------------------------------------------------------------------------- +// Flag to send a notification even if we have a delegate: +// ----------------------------------------------------------------------------- + +-(BOOL) alwaysNotify +{ + return alwaysNotify; +} + + +-(void) setAlwaysNotify: (BOOL)n +{ + alwaysNotify = n; +} + + +// ----------------------------------------------------------------------------- +// description: +// This method can be used to help in debugging. It provides the value +// used by NSLog & co. when you request to print this object using the +// %@ format specifier. +// +// REVISIONS: +// 2004-11-12 UK Created. +// ----------------------------------------------------------------------------- + +-(NSString*) description +{ + return [NSString stringWithFormat: @"%@ { watchedPaths = %@, alwaysNotify = %@ }", NSStringFromClass([self class]), watchedPaths, (alwaysNotify? @"YES" : @"NO") ]; +} + +@end + + diff --git a/src/MAC/UKMainThreadProxy.h b/src/MAC/UKMainThreadProxy.h new file mode 100644 index 0000000..725d5bc --- /dev/null +++ b/src/MAC/UKMainThreadProxy.h @@ -0,0 +1,55 @@ +/* ============================================================================= + FILE: UKMainThreadProxy.h + PROJECT: UKMainThreadProxy + + PURPOSE: Send a message to object theObject to [theObject mainThreadProxy] + instead and the message will be received on the main thread by + theObject. + + COPYRIGHT: (c) 2004 M. Uli Kusterer, all rights reserved. + + AUTHORS: M. Uli Kusterer - UK + + LICENSES: GPL, Modified BSD + + REVISIONS: + 2004-10-14 UK Created. + ========================================================================== */ + +// ----------------------------------------------------------------------------- +// Headers: +// ----------------------------------------------------------------------------- + +#import + + +// ----------------------------------------------------------------------------- +// Categories: +// ----------------------------------------------------------------------------- + +@interface NSObject (UKMainThreadProxy) + +-(id) mainThreadProxy; // You can't init or release this object. +-(id) copyMainThreadProxy; // Gives you a retained version. + +@end + + +// ----------------------------------------------------------------------------- +// Classes: +// ----------------------------------------------------------------------------- + +/* + This object is created as a proxy in a second thread for an existing object. + All messages you send to this object will automatically be sent to the other + object on the main thread, except NSObject methods like retain/release etc. +*/ + +@interface UKMainThreadProxy : NSObject +{ + IBOutlet id target; +} + +-(id) initWithTarget: (id)targ; + +@end diff --git a/src/MAC/UKMainThreadProxy.m b/src/MAC/UKMainThreadProxy.m new file mode 100644 index 0000000..32b6df4 --- /dev/null +++ b/src/MAC/UKMainThreadProxy.m @@ -0,0 +1,136 @@ +/* ============================================================================= + FILE: UKMainThreadProxy.h + PROJECT: UKMainThreadProxy + + PURPOSE: Send a message to object theObject to [theObject mainThreadProxy] + instead and the message will be received on the main thread by + theObject. + + COPYRIGHT: (c) 2004 M. Uli Kusterer, all rights reserved. + + AUTHORS: M. Uli Kusterer - UK + + LICENSES: GPL, Modified BSD + + REVISIONS: + 2004-10-14 UK Created. + ========================================================================== */ + +// ----------------------------------------------------------------------------- +// Headers: +// ----------------------------------------------------------------------------- + +#import "UKMainThreadProxy.h" + + +@implementation UKMainThreadProxy + +-(id) initWithTarget: (id)targ +{ + self = [super init]; + if( self ) + target = targ; + + return self; +} + +-(id) performSelector: (SEL)itemAction +{ + BOOL does = [super respondsToSelector: itemAction]; + if( does ) + return [super performSelector: itemAction]; + + if( ![target respondsToSelector: itemAction] ) + [self doesNotRecognizeSelector: itemAction]; + + [target retain]; + [target performSelectorOnMainThread: itemAction withObject: nil waitUntilDone: YES]; + [target release]; + + return nil; +} + + +-(id) performSelector: (SEL)itemAction withObject: (id)obj +{ + BOOL does = [super respondsToSelector: itemAction]; + if( does ) + return [super performSelector: itemAction withObject: obj]; + + if( ![target respondsToSelector: itemAction] ) + [self doesNotRecognizeSelector: itemAction]; + + [target retain]; + [obj retain]; + [target performSelectorOnMainThread: itemAction withObject: obj waitUntilDone: YES]; + [obj release]; + [target release]; + + return nil; +} + +-(BOOL) respondsToSelector: (SEL)itemAction +{ + BOOL does = [super respondsToSelector: itemAction]; + + return( does || [target respondsToSelector: itemAction] ); +} + + +// ----------------------------------------------------------------------------- +// Forwarding unknown methods to the target: +// ----------------------------------------------------------------------------- + +-(NSMethodSignature*) methodSignatureForSelector: (SEL)itemAction +{ + NSMethodSignature* sig = [super methodSignatureForSelector: itemAction]; + + if( sig ) + return sig; + + return [target methodSignatureForSelector: itemAction]; +} + +-(void) forwardInvocation: (NSInvocation*)invocation +{ + SEL itemAction = [invocation selector]; + + if( [target respondsToSelector: itemAction] ) + { + [invocation retainArguments]; + [target retain]; + [invocation performSelectorOnMainThread: @selector(invokeWithTarget:) withObject: target waitUntilDone: YES]; + [target release]; + } + else + [self doesNotRecognizeSelector: itemAction]; +} + + +-(id) mainThreadProxy // Just in case someone accidentally sends this message to a main thread proxy. +{ + return self; +} + +-(id) copyMainThreadProxy // Just in case someone accidentally sends this message to a main thread proxy. +{ + return [self retain]; +} + +@end + + +@implementation NSObject (UKMainThreadProxy) + +-(id) mainThreadProxy +{ + return [[[UKMainThreadProxy alloc] initWithTarget: self] autorelease]; +} + +-(id) copyMainThreadProxy +{ + return [[UKMainThreadProxy alloc] initWithTarget: self]; +} + +@end + diff --git a/src/MAC/WadViewController.h b/src/MAC/WadViewController.h new file mode 100644 index 0000000..d4528c8 --- /dev/null +++ b/src/MAC/WadViewController.h @@ -0,0 +1,36 @@ +// This file is hereby placed in the Public Domain -- Neil Stevens + +#import + +@interface WadViewController : NSObject +{ + // Wad options + NSMutableArray *wads; + + IBOutlet id view; + IBOutlet id removeButton; +} + +- (id)init; +- (void)dealloc; + +// UI +- (IBAction)add:(id)sender; +- (void)addEnded:(NSOpenPanel *)panel returnCode:(int)code contextInfo:(void *)info; +- (IBAction)remove:(id)sender; + +// Preferences saving +- (NSArray *)wads; +- (void)setWads:(NSArray *)newWads; + +// Table view and data source +- (void)tableViewSelectionDidChange:(NSNotification *)notification; +- (int)numberOfRowsInTableView:(NSTableView *)tableView; +- (id)tableView:(NSTableView *)tableView + objectValueForTableColumn:(NSTableColumn *)column + row:(int)row; +- (void)tableView:(NSTableView *)tableView + setObjectValue:(id)object + forTableColumn:(NSTableColumn *)column + row:(int)row; +@end diff --git a/src/MAC/WadViewController.m b/src/MAC/WadViewController.m new file mode 100644 index 0000000..49c3a36 --- /dev/null +++ b/src/MAC/WadViewController.m @@ -0,0 +1,98 @@ +// This file is hereby placed in the Public Domain -- Neil Stevens + +#import "WadViewController.h" + +@implementation WadViewController + +- (id)init +{ + if(self = [super init]) + { + wads = [[NSMutableArray arrayWithCapacity:3] retain]; + } + return self; +} + +- (void)dealloc +{ + [wads release]; + [super dealloc]; +} + +- (IBAction)add:(id)sender +{ + NSOpenPanel *panel = [NSOpenPanel openPanel]; + [panel setAllowsMultipleSelection:true]; + [panel setCanChooseFiles:true]; + [panel setCanChooseDirectories:false]; + NSArray *types = [NSArray arrayWithObjects:@"wad", @"WAD", @"DEH", @"deh", nil]; + [panel beginSheetForDirectory:nil file:nil types:types + modalForWindow:[NSApp mainWindow] modalDelegate:self + didEndSelector:@selector(addEnded:returnCode:contextInfo:) + contextInfo:nil]; +} + +- (void)addEnded:(NSOpenPanel *)panel returnCode:(int)code contextInfo:(void *)info +{ + if(code == NSCancelButton) return; + + int i; + for(i = 0; i < [[panel filenames] count]; ++i) + [wads insertObject:[[panel filenames] objectAtIndex:i] atIndex:[wads count]]; + + [view noteNumberOfRowsChanged]; +} + +- (IBAction)remove:(id)sender +{ + [wads removeObjectsAtIndexes:[view selectedRowIndexes]]; + [view selectRowIndexes:[NSIndexSet indexSetWithIndex:-1] byExtendingSelection:false]; + [view noteNumberOfRowsChanged]; +} + +- (NSArray *)wads +{ + return [NSArray arrayWithArray:wads]; +} + +- (void)setWads:(NSArray *)newWads +{ + [wads setArray:newWads]; + [view noteNumberOfRowsChanged]; + [self tableViewSelectionDidChange:nil]; +} + +- (void)tableViewSelectionDidChange:(NSNotification *)notification +{ + [removeButton setEnabled:([view selectedRow] > -1)]; +} + +- (int)numberOfRowsInTableView:(NSTableView *)tableView +{ + return [wads count]; +} + +- (id)tableView:(NSTableView *)tableView + objectValueForTableColumn:(NSTableColumn *)column + row:(int)row +{ + NSString *columnId = [column identifier]; + if([columnId isEqualToString:@"Path"]) + return [wads objectAtIndex:row]; + else if([columnId isEqualToString:@"Icon"]) + return [[NSWorkspace sharedWorkspace] iconForFile:[wads objectAtIndex:row]]; + else + return nil; +} + +- (void)tableView:(NSTableView *)tableView + setObjectValue:(id)object + forTableColumn:(NSTableColumn *)column + row:(int)row +{ + NSString *columnId = [[column identifier] stringValue]; + if([columnId isEqualToString:@"Path"]) + [wads replaceObjectAtIndex:row withObject:object]; +} + +@end diff --git a/src/MAC/i_sound.m b/src/MAC/i_sound.m new file mode 100644 index 0000000..a2e002e --- /dev/null +++ b/src/MAC/i_sound.m @@ -0,0 +1,242 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System interface for sound. + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "doomdef.h" +#include "doomstat.h" +#include "doomtype.h" + +#include "z_zone.h" + +#include "m_swap.h" +#include "i_system.h" +#include "i_sound.h" +#include "m_argv.h" +#include "m_misc.h" +#include "w_wad.h" +#include "lprintf.h" +#include "s_sound.h" + +#include "d_main.h" + +// +// MUSIC API. +// + +// placeholder for unused option +const char *snd_mididev; + +const char *midiplayers[2] = {"quicktime", NULL}; +const char *snd_midiplayer = "quicktime"; +const char *snd_soundfont; +void M_ChangeMIDIPlayer(void) +{ +} + +#import +#import +#include "mus2mid.h" + +char *music_tmp = 0; /* cph - name of music temporary file */ +QTMovie *movie = 0; +float movieVolume = 1.0; +int inLoopedMode = YES; + +void I_ShutdownMusic(void) +{ + if(movie) + { + [movie release]; + movie = 0; + } + + if (music_tmp) { + unlink(music_tmp); + lprintf(LO_DEBUG, "I_ShutdownMusic: removing %s\n", music_tmp); + free(music_tmp); + } +} + +void I_InitMusic(void) +{ + music_tmp = strdup("/tmp/prboom-music-XXXXXX"); + { + int fd = mkstemp(music_tmp); + if (fd<0) + { + lprintf(LO_ERROR, "I_InitMusic: failed to create music temp file %s", music_tmp); + unlink(music_tmp); + free(music_tmp); + return; + } + close(fd); + } + music_tmp = realloc(music_tmp, strlen(music_tmp) + 4); + strcat(music_tmp, ".mid"); + I_AtExit(I_ShutdownMusic, true); +} + +void I_PlaySong(int handle, int looping) +{ + inLoopedMode = looping ? YES : NO; + + [movie gotoBeginning]; + [movie setAttribute:[NSNumber numberWithBool:inLoopedMode] + forKey:QTMovieLoopsAttribute]; + [movie setVolume:movieVolume]; + [movie play]; +} + +void I_UpdateMusic(void) +{ +} + +void I_PauseSong (int handle) +{ + if(!movie) return; + [movie stop]; +} + +void I_ResumeSong (int handle) +{ + if(!movie) return; + [movie play]; +} + +void I_StopSong(int handle) +{ + if(!movie) return; + [movie stop]; +} + +void I_UnRegisterSong(int handle) +{ + if(!movie) return; + [movie stop]; + [movie release]; + movie = 0; +} + +int I_RegisterSong(const void *data, size_t len) +{ + FILE *midfile; + bool MidiIsReady = false; + + if ( music_tmp == NULL ) + return 0; + midfile = M_fopen(music_tmp, "wb"); + if ( midfile == NULL ) { + lprintf(LO_ERROR,"Couldn't write MIDI to %s\n", music_tmp); + return 0; + } + /* Convert MUS chunk to MIDI? */ + if ( memcmp(data, "MUS", 3) == 0 ) + { + // e6y + // New mus -> mid conversion code thanks to Ben Ryves + // This plays back a lot of music closer to Vanilla Doom - eg. tnt.wad map02 + void *outbuf; + size_t outbuf_len; + int result; + + MEMFILE *instream = mem_fopen_read((void*)data, len); + MEMFILE *outstream = mem_fopen_write(); + + result = mus2mid(instream, outstream); + + if (result == 0) + { + mem_get_buf(outstream, &outbuf, &outbuf_len); + MidiIsReady = M_WriteFile(music_tmp, outbuf, outbuf_len); + } + + mem_fclose(instream); + mem_fclose(outstream); + } else { + MidiIsReady = fwrite(data, len, 1, midfile) == 1; + } + fclose(midfile); + + if (!MidiIsReady) + { + lprintf(LO_ERROR,"Couldn't write MIDI to %s\n", music_tmp); + return 0; + } + + /* Now play in QTKit */ + NSError *error = 0; + if(movie) + { + [movie stop]; + [movie release]; + } + movie = [QTMovie movieWithFile:[NSString stringWithUTF8String:music_tmp] + error:&error]; + if(error) + { + lprintf(LO_ERROR,"Failed to create QTMovie: %s", + [[error localizedDescription] UTF8String]); + return 0; + } + + [movie retain]; + + [movie gotoBeginning]; + [movie setAttribute:[NSNumber numberWithBool:inLoopedMode] + forKey:QTMovieLoopsAttribute]; + [movie setVolume:movieVolume]; + [movie play]; + + return 1; +} + +int I_RegisterMusic( const char* filename, musicinfo_t *song ) +{ + // TODO + return 1; +} + +void I_SetMusicVolume(int value) +{ + movieVolume = (float)value / 15.0; + if(movie) + [movie setVolume:movieVolume]; +} diff --git a/src/MAC/i_system.m b/src/MAC/i_system.m new file mode 100644 index 0000000..7019ff3 --- /dev/null +++ b/src/MAC/i_system.m @@ -0,0 +1,152 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright (C) 2006) by Neil Stevens + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Misc system stuff needed by Doom, implemented for Linux. + * Mainly timer handling, and ENDOOM/ENDBOOM. + * + *----------------------------------------------------------------------------- + */ + +/* Mac path finding and WAD selection UI */ + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomdef.h" +#include "doomtype.h" +#include "dstrings.h" +#include "d_main.h" +#include "m_fixed.h" +#include "i_system.h" + +#import +#import +#import +#import +#import + +static NSString *libraryDir(void) +{ + return [@"~/Library/Application Support/PrBoom-Plus" stringByExpandingTildeInPath]; +} + +static char *NSStringToCString(NSString *str) +{ + char *cStr = malloc([str lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1); + strcpy(cStr, [str UTF8String]); + return cStr; +} + +static char *NSStringToCStringStatic(NSString *str) +{ + static char *cStr = 0; + static size_t cStrLen = 0; + + int len = [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; + + if(!cStr) + { + cStr = malloc(len); + cStrLen = len; + } + else if(cStrLen < len) + { + free(cStr); + cStr = malloc(len); + cStrLen = len; + } + + strcpy(cStr, [str UTF8String]); + return cStr; +} + +static char *macExeDir = 0; +const char *I_DoomExeDir(void) +{ + if(macExeDir) + return macExeDir; + + NSString *exeDir = libraryDir(); + [[NSFileManager defaultManager] createDirectoryAtPath:exeDir attributes:nil]; + + macExeDir = NSStringToCString(exeDir); + return macExeDir; +} + +const char* I_GetTempDir(void) +{ + if(macExeDir) + return macExeDir; + + NSString *exeDir = libraryDir(); + [[NSFileManager defaultManager] createDirectoryAtPath:exeDir attributes:nil]; + + macExeDir = NSStringToCString(exeDir); + return macExeDir; +} + +char *I_FindFileInternal(const char *wf_name, const char *ext, bool isStatic) +{ + NSArray *paths = [NSArray arrayWithObject:libraryDir()]; + paths = [paths arrayByAddingObject: [[NSBundle mainBundle] resourcePath]]; + paths = [paths arrayByAddingObject: @""]; + + char *retval = 0; + int i; + for(i = 0; !retval && (i < [paths count]); ++i) + { + NSString *path = [NSString stringWithFormat:@"%@/%@", + [paths objectAtIndex:i], + [NSString stringWithUTF8String:wf_name]]; + + + if([[NSFileManager defaultManager] isReadableFileAtPath:path]) + { + if(isStatic) + retval = NSStringToCStringStatic(path); + else + retval = NSStringToCString(path); + } + } + + return retval; +} + +char *I_FindFile(const char *wf_name, const char *ext) +{ + return I_FindFileInternal(wf_name, ext, false); +} + +const char *I_FindFile2(const char *wf_name, const char *ext) +{ + return I_FindFileInternal(wf_name, ext, true); +} diff --git a/src/MUSIC/alsaplayer.c b/src/MUSIC/alsaplayer.c new file mode 100644 index 0000000..30e6730 --- /dev/null +++ b/src/MUSIC/alsaplayer.c @@ -0,0 +1,1013 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * + * Copyright + * (C) 2011 by Nicholai Main + * (C) 2021 by Gustavo Rehermann + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +// TODO: some duplicated code with this and the fluidsynth and portmidi +// players should be split off or something + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "musicplayer.h" + +#ifndef HAVE_ALSA +#include + +static const char *alsa_name (void) +{ + return "alsa midi player (DISABLED)"; +} + + +static int alsa_init (int samplerate) +{ + return 0; +} + +const music_player_t alsa_player = +{ + alsa_name, + alsa_init, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +#else // HAVE_ALSA + +#include "alsaplayer.h" +#include +#include +#include +#include "lprintf.h" +#include "midifile.h" +#include "i_sound.h" // for snd_mididev + +#define CHK_RET(stmt, msg) if((stmt) < 0) { return (msg); } +#define CHK_LPRINT(stmt, ltype, ...) if((stmt) < 0) { lprintf(ltype, __VA_ARGS__); } +#define CHK_LPRINT_ERR(stmt, ltype, ...) { alsaplayer_err = (stmt); if(alsaplayer_err < 0) { lprintf(ltype, __VA_ARGS__, snd_strerror(alsaplayer_err)); } } +#define CHK_LPRINT_RET(stmt, ret, ltype, ...) if((stmt) < 0) { lprintf(ltype, __VA_ARGS__); return (ret); } +#define CHK_LPRINT_ERR_RET(stmt, ret, ltype, ...) { alsaplayer_err = (stmt); if(alsaplayer_err < 0) { lprintf(ltype, __VA_ARGS__, snd_strerror(alsaplayer_err)); return ret; } } + +static midi_event_t **events; +static int eventpos; +static midi_file_t *midifile; + +static int alsa_playing; +static int alsa_paused; +static int alsa_looping; +static int alsa_volume; +static int alsa_open = 0; + +// if set to 0, can auto-connect to default port +static int alsa_first_connected = 0; + +static double spmc; +static double alsa_delta; + +static unsigned long trackstart; + +static snd_seq_t *seq_handle = NULL; +snd_seq_event_t seq_ev; +static int out_id; +static int out_port; +static int out_queue; + +#define SYSEX_BUFF_SIZE 1024 +static unsigned char sysexbuff[SYSEX_BUFF_SIZE]; +static int sysexbufflen; + +int alsaplayer_err; + +static snd_seq_queue_status_t *queue_status; + +//////////////////// + +// alsa output list functionality + +int alsaplayer_num_outs; +alsaplay_output_t alsaplayer_outputs[64]; + +void alsaplay_clear_outputs(void) { + // clear output list + alsaplayer_num_outs = 0; +} + +static snd_seq_client_info_t *cinfo; +static snd_seq_port_info_t *pinfo; + +void alsaplay_refresh_outputs(void) { + // port type and capabilities required from valid MIDI output + const int OUT_CAPS_DESIRED = (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE); + + snd_seq_client_info_malloc(&cinfo); + snd_seq_port_info_malloc (&pinfo); + + if (!seq_handle) + { + lprintf(LO_WARN, "alsaplay_refresh_outputs: Can't list ALSA output ports: seq_handle is not initialized\n"); + return; + } + + alsaplay_clear_outputs(); + + // clear client info + snd_seq_client_info_set_client(cinfo, -1); + + while (snd_seq_query_next_client(seq_handle, cinfo) == 0) + { + // list ports of each client + + int client_num = snd_seq_client_info_get_client(cinfo); + + if (client_num == out_id) + { + // skip self + continue; + } + + if (!snd_seq_client_info_get_num_ports(cinfo)) + { + // skip clients without ports + continue; + } + + // clear port info + snd_seq_port_info_set_client(pinfo, client_num); + snd_seq_port_info_set_port(pinfo, -1); + + while (snd_seq_query_next_port(seq_handle, pinfo) == 0) + { + int port_num = snd_seq_port_info_get_port(pinfo); + int out_ind; + const char *client_name; + + // check if port is valid midi output + + if (!(snd_seq_port_info_get_type(pinfo) & SND_SEQ_PORT_TYPE_MIDI_GENERIC)) + { + continue; + } + + if ((snd_seq_port_info_get_capability(pinfo) & OUT_CAPS_DESIRED) != OUT_CAPS_DESIRED) + { + continue; + } + + // add to outputs list + + out_ind = alsaplayer_num_outs++; + + alsaplayer_outputs[out_ind].client = client_num; + alsaplayer_outputs[out_ind].port = port_num; + + client_name = snd_seq_client_info_get_name(cinfo); + + lprintf(LO_INFO, "alsaplay_refresh_outputs: output #%d: (%d:%d) %s\n", out_ind, client_num, port_num, client_name); + + // client name only up to 100 chars, so it always fits within a 120 byte buffer + sprintf(alsaplayer_outputs[out_ind].name, "%.*s (%d:%d)", 100, client_name, client_num, port_num); + } + } + + snd_seq_client_info_free(cinfo); + snd_seq_port_info_free (pinfo); +} + +int alsaplay_connect_output(int which) { + if (which >= alsaplayer_num_outs) + { + lprintf(LO_WARN, "alsaplay_connect_output: tried to connect to output listing at index out of bounds: %d\n", which); + return -1; + } + + return alsa_midi_set_dest(alsaplayer_outputs[which].client, alsaplayer_outputs[which].port); +} + +const char *alsaplay_get_output_name(int which) { + if (seq_handle == NULL) + { + return NULL; + } + + if (which >= alsaplayer_num_outs) + { + return NULL; + } + + return alsaplayer_outputs[which].name; +} + +//////////////////// + +// alsa utility functions + +int alsa_midi_default_dest (void) +{ + static int status; // alsa error code + static int code; // *our* error code, for control flow + + static const char *loopback_check_name = "MIDI THROUGH"; // uppercase for comparison + static const signed char upper_diff = 'A' - 'a'; + const int loopback_check_len = strlen(loopback_check_name); + + // port type and capabilities required from valid MIDI output + const int OUT_CAPS_DESIRED = (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE); + + int loopback_cl = -1, loopback_prt = 0; + const char *loopback_name; + + snd_seq_client_info_malloc(&cinfo); + snd_seq_port_info_malloc (&pinfo); + + if (!seq_handle) + { + lprintf(LO_WARN, "alsa_midi_default_dest: Can't list ALSA output ports: seq_handle is not initialized\n"); + return 0; + } + + alsaplay_clear_outputs(); + + // clear client info + snd_seq_client_info_set_client(cinfo, -1); + + while (snd_seq_query_next_client(seq_handle, cinfo) == 0) + { + // list ports of each client + + int client_num = snd_seq_client_info_get_client(cinfo); + const char *client_name; + + if (client_num == out_id) + { + // skip self + continue; + } + + if (!snd_seq_client_info_get_num_ports(cinfo)) + { + // skip clients without ports + continue; + } + + client_name = snd_seq_client_info_get_name(cinfo); + + if (strlen(client_name) >= loopback_check_len) + { + // check for and skip loopback (eg MIDI Through) + + int i; + + for (i = 0; i < loopback_check_len; i++) + { + char a = client_name[i]; + a = (a < 'a') || (a > 'z') ? a : a + upper_diff; + + if (a != loopback_check_name[i]) + { + break; + } + } + + if (i == loopback_check_len) + { + loopback_cl = client_num; + loopback_name = client_name; + + continue; + } + } + + // clear port info + snd_seq_port_info_set_client(pinfo, client_num); + snd_seq_port_info_set_port(pinfo, -1); + + while (snd_seq_query_next_port(seq_handle, pinfo) == 0) + { + int port_num = snd_seq_port_info_get_port(pinfo); + + // check if port is valid midi output + + if (!(snd_seq_port_info_get_type(pinfo) & SND_SEQ_PORT_TYPE_MIDI_GENERIC)) + { + continue; + } + + if ((snd_seq_port_info_get_capability(pinfo) & OUT_CAPS_DESIRED) != OUT_CAPS_DESIRED) + { + continue; + } + + if (loopback_cl == client_num) + { + // save as midi through port + + loopback_prt = port_num; + break; + } + + else + { + // connect to this port + + if (alsa_midi_set_dest(client_num, port_num) != 0) + { + lprintf(LO_WARN, "alsa_midi_default_dest: error connecting to default port %i:%i (%s): %s\n", client_num, port_num, client_name, snd_strerror(alsaplayer_err)); + + code = 0; + goto cleanup; + } + + lprintf(LO_INFO, "alsa_midi_default_dest: connected to default port %i:%i (%s)\n", client_num, port_num, client_name); + + code = 1; + goto cleanup; + } + } + } + + // try midi through as last resort fallback + + if (loopback_cl != -1) + { + if ((status = alsa_midi_set_dest(loopback_cl, loopback_prt)) != 0) + { + lprintf(LO_WARN, "alsa_midi_default_dest: (fallback) error connecting to default port %i:%i (%s): %s\n", loopback_cl, loopback_prt, loopback_name, snd_strerror(status)); + code = 0; + goto cleanup; + } + + lprintf(LO_INFO, "alsa_midi_default_dest: (fallback) connected to default port %i:%i (%s)\n", loopback_cl, loopback_prt, loopback_name); + + code = 1; + goto cleanup; + } + + // no default port + lprintf(LO_WARN, "alsa_midi_default_dest: no default port found\n"); + + code = 0; + +cleanup: + snd_seq_client_info_free(cinfo); + snd_seq_port_info_free (pinfo); + + return code; +} + +static const char *alsa_midi_open (void) +{ + CHK_RET(snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_OUTPUT, 0), + "could not open sequencer") + + CHK_RET(snd_seq_set_client_name(seq_handle, "PrBoom+ MIDI"), + "could not set client name") + + out_id = snd_seq_client_id(seq_handle); + + CHK_RET( + out_port = snd_seq_create_simple_port(seq_handle, "Music", + SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ | SND_SEQ_PORT_CAP_READ, + SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_SOFTWARE + ), + "could not open alsa port") + + out_queue = snd_seq_alloc_named_queue(seq_handle, "prboom music queue"); + + snd_seq_queue_status_malloc(&queue_status); + + alsa_open = 1; + return NULL; +} + +int alsa_midi_set_dest (int client, int port) +{ + static int last_client = -1, last_port = 0; + + if (!seq_handle) { + return -2; + } + + // disconnect if previously connected + + if (last_client != 0) { + snd_seq_disconnect_to(seq_handle, out_port, last_client, last_port); + + last_client = client; + last_port = port; + } + + // connects to a destination alsa-midi client and port + + CHK_LPRINT_ERR_RET(snd_seq_connect_to(seq_handle, out_port, client, port), -3, + LO_WARN, "alsa_midi_set_dest: error connecting to (%d:%d): %s", last_client, last_port); + + alsa_first_connected = 1; + return 0; +} + +static unsigned long alsa_now (void) +{ + // get current position in millisecs + + const snd_seq_real_time_t *time; + + // update queue status + CHK_LPRINT_ERR_RET(snd_seq_get_queue_status(seq_handle, out_queue, queue_status), 0, + LO_WARN, "alsaplayer: alsa_now(): error getting queue status: %s\n"); + + time = snd_seq_queue_status_get_real_time(queue_status); + + if (time == 0) { + lprintf (LO_WARN, "alsaplayer: alsa_now(): error getting realtime position from queue status\n"); + return 0; + } + + return time->tv_sec * 1000 + (time->tv_nsec / 1000000); // (s,ns) to ms +} + +//////////////////// + +// alsa player callbacks + + +static const snd_seq_real_time_t *alsa_now_realtime (void) +{ + // get current position in millisecs + + const snd_seq_real_time_t *time; + + // update queue status + CHK_LPRINT_ERR_RET(snd_seq_get_queue_status(seq_handle, out_queue, queue_status), 0, + LO_WARN, "alsaplayer: alsa_now(): error getting queue status: %s\n"); + + time = snd_seq_queue_status_get_real_time(queue_status); + + if (time == 0) { + lprintf (LO_WARN, "alsaplayer: alsa_now(): error getting realtime position from queue status\n"); + } + + return time; +} + +static void alsa_midi_evt_start (unsigned long when) +{ + snd_seq_ev_clear(&seq_ev); + + // source + snd_seq_ev_set_source(&seq_ev, out_port); + + // schedule + if (when != 0) { + snd_seq_real_time_t rtime; + + // ms into (s,ns) + rtime.tv_sec = when / 1000; + rtime.tv_nsec = (when % 1000) * 1000000; + + snd_seq_ev_schedule_real(&seq_ev, out_queue, 0, &rtime); + } + + else { + snd_seq_ev_schedule_real(&seq_ev, out_queue, 0, alsa_now_realtime()); + } + + // priority + snd_seq_ev_set_priority(&seq_ev, 0); + + // destination + snd_seq_ev_set_subs(&seq_ev); +} + +static void alsa_midi_evt_finish () +{ + CHK_LPRINT_ERR(snd_seq_event_output(seq_handle, &seq_ev), + LO_WARN, "alsa_midi_evt_finish: could not output alsa midi event: %s\n"); +} + +static void alsa_midi_evt_flush () +{ + CHK_LPRINT_ERR(snd_seq_drain_output(seq_handle), + LO_WARN, "alsa_midi_evt_finish: could not drain alsa sequencer output: %s\n"); +} + +static void alsa_midi_write_event (unsigned long when, midi_event_type_t type, int channel, int v1, int v2) +{ + // ported from portmidiplayer.c (no pun intended!) + alsa_midi_evt_start(when); + + // set event value fields + switch(type) { + case MIDI_EVENT_NOTE_OFF: + snd_seq_ev_set_noteoff(&seq_ev, channel, v1, v2); + break; + + case MIDI_EVENT_NOTE_ON: + snd_seq_ev_set_noteon(&seq_ev, channel, v1, v2); + break; + + case MIDI_EVENT_AFTERTOUCH: + snd_seq_ev_set_keypress(&seq_ev, channel, v1, v2); + break; + + case MIDI_EVENT_PROGRAM_CHANGE: + snd_seq_ev_set_pgmchange(&seq_ev, channel, v1); + break; + + case MIDI_EVENT_CHAN_AFTERTOUCH: + snd_seq_ev_set_chanpress(&seq_ev, channel, v1); + break; + + case MIDI_EVENT_PITCH_BEND: + snd_seq_ev_set_pitchbend(&seq_ev, channel, v1 << 8 | v2); + break; + + case MIDI_EVENT_CONTROLLER: + snd_seq_ev_set_controller(&seq_ev, channel, v1, v2); + break; + + default: + // unknown type + lprintf(LO_WARN, "alsa_midi_write_event: unknown midi event type: %d\n", type); + return; + } + + alsa_midi_evt_finish(); +} + +static void alsa_midi_write_control (unsigned long when, int channel, int v1, int v2) +{ + alsa_midi_write_event(when, MIDI_EVENT_CONTROLLER, channel, v1, v2); +} + +static void alsa_midi_write_control_now (int channel, int v1, int v2) +{ + // send event now, disregarding 'when' + alsa_midi_write_control(0, channel, v1, v2); +} + +static void alsa_midi_all_notes_off_chan (int channel) +{ + alsa_midi_write_control_now(channel, 123, 0); + alsa_midi_evt_flush(); +} + +static void alsa_midi_all_notes_off (void) +{ + // sends All Notes Off event in all channels + for (int i = 0; i < 16; i++) { + alsa_midi_all_notes_off_chan(i); + } +} + +static int alsa_midi_init_connect_default_port (void) +{ + // load MIDI device specified in config + + if (snd_mididev && strlen(snd_mididev)) + { + snd_seq_addr_t seqaddr; + + CHK_LPRINT_ERR(snd_seq_parse_address(seq_handle, &seqaddr, snd_mididev), + LO_WARN, "alsa_init: Error connecting to configured MIDI output port \"%s\": %s", snd_mididev) + + return alsa_midi_set_dest(seqaddr.client, seqaddr.port) == 0; + } + + else + { + // connect to default + return alsa_midi_default_dest(); + } +} + +//////////////////// + +// alsa player callbacks + +static const char *alsa_name (void) +{ + return "alsa midi player"; +} + +static int alsa_init (int samplerate) +{ + const char *msg = alsa_midi_open(); + + lprintf (LO_INFO, "alsaplayer: Trying to open ALSA output port\n"); + + if (msg != NULL) { + lprintf(LO_WARN, "alsa_init: alsa_midi_open() failed: %s\n", msg); + return 0; + } + + lprintf (LO_INFO, "alsaplayer: Successfully opened port: %d\n", out_port); + + alsaplay_refresh_outputs(); // make output list and print it out + + return 1; +} + +static void alsa_shutdown (void) +{ + if (seq_handle) { + alsa_midi_all_notes_off(); + alsa_midi_evt_flush(); + + snd_seq_free_queue(seq_handle, out_queue); + snd_seq_queue_status_free(queue_status); + + snd_seq_delete_simple_port(seq_handle, out_port); + snd_seq_close(seq_handle); + + seq_handle = NULL; + } + + alsa_open = 0; +} + +static const void *alsa_registersong (const void *data, unsigned len) +{ + midimem_t mf; + + mf.len = len; + mf.pos = 0; + mf.data = (const byte*)data; + + midifile = MIDI_LoadFile (&mf); + + if (!midifile) + { + lprintf (LO_WARN, "alsa_registersong: Failed to load MIDI.\n"); + return NULL; + } + + events = MIDI_GenerateFlatList (midifile); + if (!events) + { + MIDI_FreeFile (midifile); + return NULL; + } + eventpos = 0; + + // implicit 120BPM (this is correct to spec) + //spmc = compute_spmc (MIDI_GetFileTimeDivision (midifile), 500000, 1000); + spmc = MIDI_spmc (midifile, NULL, 1000); + + // handle not used + return data; +} + +static int channelvol[16]; + +static void alsa_setchvolume (int ch, int v, unsigned long when) +{ + channelvol[ch] = v; + alsa_midi_write_control(when, ch, 7, channelvol[ch] * alsa_volume / 15); + alsa_midi_evt_flush(); +} + +static void alsa_refreshvolume (void) +{ + int i; + + for (i = 0; i < 16; i ++) + alsa_midi_write_control_now(i, 7, channelvol[i] * alsa_volume / 15); + + alsa_midi_evt_flush(); +} + +static void alsa_clearchvolume (void) +{ + int i; + for (i = 0; i < 16; i++) + channelvol[i] = 127; // default: max +} + +static void alsa_setvolume (int v) +{ + static int firsttime = 1; + + if (alsa_volume == v && !firsttime) + return; + firsttime = 0; + + alsa_volume = v; + + alsa_refreshvolume (); +} + + +static void alsa_unregistersong (const void *handle) +{ + if (events) + { + MIDI_DestroyFlatList (events); + events = NULL; + } + if (midifile) + { + MIDI_FreeFile (midifile); + midifile = NULL; + } +} + +static void alsa_pause (void) +{ + int i; + alsa_paused = 1; + alsa_midi_all_notes_off(); + + snd_seq_stop_queue(seq_handle, out_queue, 0); +} + +static void alsa_resume (void) +{ + alsa_paused = 0; + trackstart = alsa_now (); + + snd_seq_continue_queue(seq_handle, out_queue, 0); +} + +static void alsa_play (const void *handle, int looping) +{ + snd_seq_queue_timer_t *timer; + int status; + + // reinit queue + + if (!alsa_first_connected) + { + // connect to default port if haven't connected at least once yet + alsa_midi_init_connect_default_port(); + } + + if (out_queue) { + snd_seq_free_queue(seq_handle, out_queue); + snd_seq_queue_status_free(queue_status); + } + + // make queue + out_queue = snd_seq_alloc_named_queue(seq_handle, "prboom music queue"); + + snd_seq_queue_status_malloc(&queue_status); + + // set queue resolution + + snd_seq_queue_timer_malloc(&timer); + + status = snd_seq_get_queue_timer(seq_handle, out_queue, timer); + + if (status < 0) + { + lprintf(LO_WARN, "alsa_play: error getting sched queue timer: %s\n", snd_strerror(status)); + + snd_seq_queue_timer_free(timer); + goto finish; + } + + snd_seq_queue_timer_set_resolution(timer, 1000000 / 32); // 1000000 ns = 1 ms, so this is 1/32 ms + + status = snd_seq_set_queue_timer(seq_handle, out_queue, timer); + + if (status < 0) + { + lprintf(LO_WARN, "alsa_play: error setting sched queue timer with new resolution: %s\n", snd_strerror(status)); + + snd_seq_queue_timer_free(timer); + goto finish; + } + + lprintf(LO_INFO, "alsa_play: success\n"); + + snd_seq_queue_timer_free(timer); + +finish: + // initialize state stuff + eventpos = 0; + alsa_looping = looping; + alsa_playing = 1; + //alsa_paused = 0; + alsa_delta = 0.0; + alsa_clearchvolume (); + alsa_refreshvolume (); + trackstart = alsa_now (); + + // start scheduling queue + snd_seq_start_queue(seq_handle, out_queue, 0); +} + + +static void alsa_midi_writesysex (unsigned long when, int etype, unsigned char *data, int len) +{ + // sysex code is untested + // it's possible to use an auto-resizing buffer here, but a malformed + // midi file could make it grow arbitrarily large (since it must grow + // until it hits an 0xf7 terminator) + if (len + sysexbufflen > SYSEX_BUFF_SIZE) + { + lprintf (LO_WARN, "alsaplayer: ignoring large or malformed sysex message\n"); + sysexbufflen = 0; + return; + } + memcpy (sysexbuff + sysexbufflen, data, len); + sysexbufflen += len; + if (sysexbuff[sysexbufflen - 1] == 0xf7) // terminator + { + alsa_midi_evt_start(when); + snd_seq_ev_set_sysex(&seq_ev, sysexbufflen, sysexbuff); + alsa_midi_evt_finish(); + + sysexbufflen = 0; + } +} + +static void alsa_stop (void) +{ + int i; + alsa_playing = 0; + + + // songs can be stopped at any time, so reset everything + for (i = 0; i < 16; i++) + { + alsa_midi_write_control_now(i, 123, 0); // all notes off + alsa_midi_write_control_now(i, 121, 0); // reset all parameters + + // RPN sequence to adjust pitch bend range (RPN value 0x0000) + alsa_midi_write_control_now(i, 0x65, 0x00); + alsa_midi_write_control_now(i, 0x64, 0x00); + // reset pitch bend range to central tuning +/- 2 semitones and 0 cents + alsa_midi_write_control_now(i, 0x06, 0x02); + alsa_midi_write_control_now(i, 0x26, 0x00); + // end of RPN sequence + alsa_midi_write_control_now(i, 0x64, 0x7f); + alsa_midi_write_control_now(i, 0x65, 0x7f); + } + alsa_midi_evt_flush(); + // abort any partial sysex + sysexbufflen = 0; + + snd_seq_stop_queue(seq_handle, out_queue, 0); +} + +static void alsa_render (void *vdest, unsigned bufflen) +{ + // wherever you see samples in here, think milliseconds + + unsigned long newtime = alsa_now(); + unsigned long length = newtime - trackstart; + + //timerpos = newtime; + unsigned long when; + + midi_event_t *currevent; + + unsigned sampleswritten = 0; + unsigned samples; + + memset (vdest, 0, bufflen * 4); + + + + if (!alsa_playing || alsa_paused) + return; + + + while (1) + { + double eventdelta; + currevent = events[eventpos]; + + // how many samples away event is + eventdelta = currevent->delta_time * spmc; + + + // how many we will render (rounding down); include delta offset + samples = (unsigned) (eventdelta + alsa_delta); + + + if (samples + sampleswritten > length) + { // overshoot; render some samples without processing an event + break; + } + + + sampleswritten += samples; + alsa_delta -= samples; + + + // process event + when = trackstart + sampleswritten; + switch (currevent->event_type) + { + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + alsa_midi_writesysex (when, currevent->event_type, currevent->data.sysex.data, currevent->data.sysex.length); + break; + case MIDI_EVENT_META: // tempo is the only meta message we're interested in + if (currevent->data.meta.type == MIDI_META_SET_TEMPO) + spmc = MIDI_spmc (midifile, currevent, 1000); + else if (currevent->data.meta.type == MIDI_META_END_OF_TRACK) + { + if (alsa_looping) + { + int i; + eventpos = 0; + alsa_delta += eventdelta; + // fix buggy songs that forget to terminate notes held over loop point + // sdl_mixer does this as well + for (i = 0; i < 16; i++) + alsa_midi_write_control(when, i, 123, 0); // all notes off + continue; + } + // stop + alsa_stop (); + return; + } + break; // not interested in most metas + case MIDI_EVENT_CONTROLLER: + if (currevent->data.channel.param1 == 7) + { // volume event + alsa_setchvolume (currevent->data.channel.channel, currevent->data.channel.param2, when); + break; + } // fall through + default: + alsa_midi_write_event (when, currevent->event_type, currevent->data.channel.channel, currevent->data.channel.param1, currevent->data.channel.param2); + break; + + } + // if the event was a "reset all controllers", we need to additionally re-fix the volume (which itself was reset) + if (currevent->event_type == MIDI_EVENT_CONTROLLER && currevent->data.channel.param1 == 121) + alsa_setchvolume (currevent->data.channel.channel, 127, when); + + // event processed so advance midiclock + alsa_delta += eventdelta; + eventpos++; + + } + + if (samples + sampleswritten > length) + { // broke due to next event being past the end of current render buffer + // finish buffer, return + samples = length - sampleswritten; + alsa_delta -= samples; // save offset + } + + trackstart = newtime; + + alsa_midi_evt_flush(); +} + +const music_player_t alsa_player = +{ + alsa_name, + alsa_init, + alsa_shutdown, + alsa_setvolume, + alsa_pause, + alsa_resume, + alsa_registersong, + alsa_unregistersong, + alsa_play, + alsa_stop, + alsa_render +}; + + +#endif // HAVE_ALSA + diff --git a/src/MUSIC/alsaplayer.h b/src/MUSIC/alsaplayer.h new file mode 100644 index 0000000..5863f99 --- /dev/null +++ b/src/MUSIC/alsaplayer.h @@ -0,0 +1,60 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * + * Copyright (C) 2021 by Gustavo Rehermann + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifndef ALSAPLAYER_H +#define ALSAPLAYER_H + +#include "musicplayer.h" + +extern const music_player_t alsa_player; + +#ifdef HAVE_ALSA + +#include + +// available outputs +typedef struct { + int client; + int port; + char name[120]; // 120 + two 32-bit (4-byte) ints = 128, nice alignment +} alsaplay_output_t; + +extern alsaplay_output_t alsaplayer_outputs[64]; +extern int alsaplayer_num_outs; +extern int alsaplayer_err; + +void alsaplay_clear_outputs(void); +void alsaplay_refresh_outputs(void); +const char *alsaplay_get_output_name(int which); +int alsaplay_connect_output(int which); +int alsa_midi_set_dest (int client, int port); + +#endif // HAVE_ALSA + +#endif // ALSAPLAYER_H diff --git a/src/MUSIC/dbopl.c b/src/MUSIC/dbopl.c new file mode 100644 index 0000000..d6a8f3c --- /dev/null +++ b/src/MUSIC/dbopl.c @@ -0,0 +1,1596 @@ +/* + * Copyright (C) 2002-2010 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +// +// Chocolate Doom-related discussion: +// +// This is the DosBox OPL emulator code (src/hardware/dbopl.cpp) r3635, +// converted to C. The bulk of the work was done using the minus-minus +// script in the Chocolate Doom SVN repository, then the result tweaked +// by hand until working. +// + + +/* + DOSBox implementation of a combined Yamaha YMF262 and Yamaha YM3812 emulator. + Enabling the opl3 bit will switch the emulator to stereo opl3 output instead of regular mono opl2 + Except for the table generation it's all integer math + Can choose different types of generators, using muls and bigger tables, try different ones for slower platforms + The generation was based on the MAME implementation but tried to have it use less memory and be faster in general + MAME uses much bigger envelope tables and this will be the biggest cause of it sounding different at times + + //TODO Don't delay first operator 1 sample in opl3 mode + //TODO Maybe not use class method pointers but a regular function pointers with operator as first parameter + //TODO Fix panning for the Percussion channels, would any opl3 player use it and actually really change it though? + //TODO Check if having the same accuracy in all frequency multipliers sounds better or not + + //DUNNO Keyon in 4op, switch to 2op without keyoff. +*/ + +/* $Id: dbopl.cpp,v 1.10 2009-06-10 19:54:51 harekiet Exp $ */ + +#include +#include +#include +//#include "dosbox.h" +#include "dbopl.h" + +#ifdef _MSC_VER +#define inline __inline +#endif + +#define GCC_UNLIKELY(x) x + +#define TRUE 1 +#define FALSE 0 + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +#define OPLRATE ((double)(14318180.0 / 288.0)) +#define TREMOLO_TABLE 52 + +//Try to use most precision for frequencies +//Else try to keep different waves in synch +//#define WAVE_PRECISION 1 +#ifndef WAVE_PRECISION +//Wave bits available in the top of the 32bit range +//Original adlib uses 10.10, we use 10.22 +#define WAVE_BITS 10 +#else +//Need some extra bits at the top to have room for octaves and frequency multiplier +//We support to 8 times lower rate +//128 * 15 * 8 = 15350, 2^13.9, so need 14 bits +#define WAVE_BITS 14 +#endif +#define WAVE_SH ( 32 - WAVE_BITS ) +#define WAVE_MASK ( ( 1 << WAVE_SH ) - 1 ) + +//Use the same accuracy as the waves +#define LFO_SH ( WAVE_SH - 10 ) +//LFO is controlled by our tremolo 256 sample limit +#define LFO_MAX ( 256 << ( LFO_SH ) ) + + +//Maximum amount of attenuation bits +//Envelope goes to 511, 9 bits +#if (DBOPL_WAVE == WAVE_TABLEMUL ) +//Uses the value directly +#define ENV_BITS ( 9 ) +#else +//Add 3 bits here for more accuracy and would have to be shifted up either way +#define ENV_BITS ( 9 ) +#endif +//Limits of the envelope with those bits and when the envelope goes silent +#define ENV_MIN 0 +#define ENV_EXTRA ( ENV_BITS - 9 ) +#define ENV_MAX ( 511 << ENV_EXTRA ) +#define ENV_LIMIT ( ( 12 * 256) >> ( 3 - ENV_EXTRA ) ) +#define ENV_SILENT( _X_ ) ( (_X_) >= ENV_LIMIT ) + +//Attack/decay/release rate counter shift +#define RATE_SH 24 +#define RATE_MASK ( ( 1 << RATE_SH ) - 1 ) +//Has to fit within 16bit lookuptable +#define MUL_SH 16 + +//Check some ranges +#if ENV_EXTRA > 3 +#error Too many envelope bits +#endif + +static inline void Operator__SetState(Operator *self, Bit8u s ); +static inline Bit32u Chip__ForwardNoise(Chip *self); + +// C++'s template<> sure is useful sometimes. + +static Channel* Channel__BlockTemplate(Channel *self, Chip* chip, + Bit32u samples, Bit32s* output, + SynthMode mode ); +#define BLOCK_TEMPLATE(mode) \ + static Channel* Channel__BlockTemplate_ ## mode(Channel *self, Chip* chip, \ + Bit32u samples, Bit32s* output) \ + { \ + return Channel__BlockTemplate(self, chip, samples, output, mode); \ + } + +BLOCK_TEMPLATE(sm2AM) +BLOCK_TEMPLATE(sm2FM) +BLOCK_TEMPLATE(sm3AM) +BLOCK_TEMPLATE(sm3FM) +BLOCK_TEMPLATE(sm3FMFM) +BLOCK_TEMPLATE(sm3AMFM) +BLOCK_TEMPLATE(sm3FMAM) +BLOCK_TEMPLATE(sm3AMAM) +BLOCK_TEMPLATE(sm2Percussion) +BLOCK_TEMPLATE(sm3Percussion) + +//How much to substract from the base value for the final attenuation +static const Bit8u KslCreateTable[16] = { + //0 will always be be lower than 7 * 8 + 64, 32, 24, 19, + 16, 12, 11, 10, + 8, 6, 5, 4, + 3, 2, 1, 0, +}; + +#define M(_X_) ((Bit8u)( (_X_) * 2)) +static const Bit8u FreqCreateTable[16] = { + M(0.5), M(1 ), M(2 ), M(3 ), M(4 ), M(5 ), M(6 ), M(7 ), + M(8 ), M(9 ), M(10), M(10), M(12), M(12), M(15), M(15) +}; +#undef M + +//We're not including the highest attack rate, that gets a special value +static const Bit8u AttackSamplesTable[13] = { + 69, 55, 46, 40, + 35, 29, 23, 20, + 19, 15, 11, 10, + 9 +}; +//On a real opl these values take 8 samples to reach and are based upon larger tables +static const Bit8u EnvelopeIncreaseTable[13] = { + 4, 5, 6, 7, + 8, 10, 12, 14, + 16, 20, 24, 28, + 32, +}; + +#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) +static Bit16u ExpTable[ 256 ]; +#endif + +#if ( DBOPL_WAVE == WAVE_HANDLER ) +//PI table used by WAVEHANDLER +static Bit16u SinTable[ 512 ]; +#endif + +#if ( DBOPL_WAVE > WAVE_HANDLER ) +//Layout of the waveform table in 512 entry intervals +//With overlapping waves we reduce the table to half it's size + +// | |//\\|____|WAV7|//__|/\ |____|/\/\| +// |\\//| | |WAV7| | \/| | | +// |06 |0126|17 |7 |3 |4 |4 5 |5 | + +//6 is just 0 shifted and masked + +static Bit16s WaveTable[ 8 * 512 ]; +//Distance into WaveTable the wave starts +static const Bit16u WaveBaseTable[8] = { + 0x000, 0x200, 0x200, 0x800, + 0xa00, 0xc00, 0x100, 0x400, + +}; +//Mask the counter with this +static const Bit16u WaveMaskTable[8] = { + 1023, 1023, 511, 511, + 1023, 1023, 512, 1023, +}; + +//Where to start the counter on at keyon +static const Bit16u WaveStartTable[8] = { + 512, 0, 0, 0, + 0, 512, 512, 256, +}; +#endif + +#if ( DBOPL_WAVE == WAVE_TABLEMUL ) +static Bit16u MulTable[ 384 ]; +#endif + +static Bit8u KslTable[ 8 * 16 ]; +static Bit8u TremoloTable[ TREMOLO_TABLE ]; +//Start of a channel behind the chip struct start +static Bit16u ChanOffsetTable[32]; +//Start of an operator behind the chip struct start +static Bit16u OpOffsetTable[64]; + +//The lower bits are the shift of the operator vibrato value +//The highest bit is right shifted to generate -1 or 0 for negation +//So taking the highest input value of 7 this gives 3, 7, 3, 0, -3, -7, -3, 0 +static const Bit8s VibratoTable[ 8 ] = { + 1 - 0x00, 0 - 0x00, 1 - 0x00, 30 - 0x00, + 1 - 0x80, 0 - 0x80, 1 - 0x80, 30 - 0x80 +}; + +//Shift strength for the ksl value determined by ksl strength +static const Bit8u KslShiftTable[4] = { + 31,1,2,0 +}; + +//Generate a table index and table shift value using input value from a selected rate +static void EnvelopeSelect( Bit8u val, Bit8u *index, Bit8u *shift ) { + if ( val < 13 * 4 ) { //Rate 0 - 12 + *shift = 12 - ( val >> 2 ); + *index = val & 3; + } else if ( val < 15 * 4 ) { //rate 13 - 14 + *shift = 0; + *index = val - 12 * 4; + } else { //rate 15 and up + *shift = 0; + *index = 12; + } +} + +#if ( DBOPL_WAVE == WAVE_HANDLER ) +/* + Generate the different waveforms out of the sine/exponetial table using handlers +*/ +static inline Bits MakeVolume( Bitu wave, Bitu volume ) { + Bitu total = wave + volume; + Bitu index = total & 0xff; + Bitu sig = ExpTable[ index ]; + Bitu exp = total >> 8; +#if 0 + //Check if we overflow the 31 shift limit + if ( exp >= 32 ) { + LOG_MSG( "WTF %d %d", total, exp ); + } +#endif + return (sig >> exp); +}; + +static Bits DB_FASTCALL WaveForm0( Bitu i, Bitu volume ) { + Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 + Bitu wave = SinTable[i & 511]; + return (MakeVolume( wave, volume ) ^ neg) - neg; +} +static Bits DB_FASTCALL WaveForm1( Bitu i, Bitu volume ) { + Bit32u wave = SinTable[i & 511]; + wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm2( Bitu i, Bitu volume ) { + Bitu wave = SinTable[i & 511]; + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm3( Bitu i, Bitu volume ) { + Bitu wave = SinTable[i & 255]; + wave |= ( ( (i ^ 256 ) & 256) - 1) >> ( 32 - 12 ); + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm4( Bitu i, Bitu volume ) { + //Twice as fast + i <<= 1; + Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 + Bitu wave = SinTable[i & 511]; + wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); + return (MakeVolume( wave, volume ) ^ neg) - neg; +} +static Bits DB_FASTCALL WaveForm5( Bitu i, Bitu volume ) { + //Twice as fast + i <<= 1; + Bitu wave = SinTable[i & 511]; + wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm6( Bitu i, Bitu volume ) { + Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 + return (MakeVolume( 0, volume ) ^ neg) - neg; +} +static Bits DB_FASTCALL WaveForm7( Bitu i, Bitu volume ) { + //Negative is reversed here + Bits neg = (( i >> 9) & 1) - 1; + Bitu wave = (i << 3); + //When negative the volume also runs backwards + wave = ((wave ^ neg) - neg) & 4095; + return (MakeVolume( wave, volume ) ^ neg) - neg; +} + +static const WaveHandler WaveHandlerTable[8] = { + WaveForm0, WaveForm1, WaveForm2, WaveForm3, + WaveForm4, WaveForm5, WaveForm6, WaveForm7 +}; + +#endif + +/* + Operator +*/ + +//We zero out when rate == 0 +static inline void Operator__UpdateAttack(Operator *self, const Chip* chip ) { + Bit8u rate = self->reg60 >> 4; + if ( rate ) { + Bit8u val = (rate << 2) + self->ksr; + self->attackAdd = chip->attackRates[ val ]; + self->rateZero &= ~(1 << ATTACK); + } else { + self->attackAdd = 0; + self->rateZero |= (1 << ATTACK); + } +} +static inline void Operator__UpdateDecay(Operator *self, const Chip* chip ) { + Bit8u rate = self->reg60 & 0xf; + if ( rate ) { + Bit8u val = (rate << 2) + self->ksr; + self->decayAdd = chip->linearRates[ val ]; + self->rateZero &= ~(1 << DECAY); + } else { + self->decayAdd = 0; + self->rateZero |= (1 << DECAY); + } +} +static inline void Operator__UpdateRelease(Operator *self, const Chip* chip ) { + Bit8u rate = self->reg80 & 0xf; + if ( rate ) { + Bit8u val = (rate << 2) + self->ksr; + self->releaseAdd = chip->linearRates[ val ]; + self->rateZero &= ~(1 << RELEASE); + if ( !(self->reg20 & MASK_SUSTAIN ) ) { + self->rateZero &= ~( 1 << SUSTAIN ); + } + } else { + self->rateZero |= (1 << RELEASE); + self->releaseAdd = 0; + if ( !(self->reg20 & MASK_SUSTAIN ) ) { + self->rateZero |= ( 1 << SUSTAIN ); + } + } +} + +static inline void Operator__UpdateAttenuation(Operator *self) { + Bit8u kslBase = (Bit8u)((self->chanData >> SHIFT_KSLBASE) & 0xff); + Bit32u tl = self->reg40 & 0x3f; + Bit8u kslShift = KslShiftTable[ self->reg40 >> 6 ]; + //Make sure the attenuation goes to the right bits + self->totalLevel = tl << ( ENV_BITS - 7 ); //Total level goes 2 bits below max + self->totalLevel += ( kslBase << ENV_EXTRA ) >> kslShift; +} + +static void Operator__UpdateFrequency(Operator *self) { + Bit32u freq = self->chanData & (( 1 << 10 ) - 1); + Bit32u block = (self->chanData >> 10) & 0xff; +#ifdef WAVE_PRECISION + block = 7 - block; + self->waveAdd = ( freq * self->freqMul ) >> block; +#else + self->waveAdd = ( freq << block ) * self->freqMul; +#endif + if ( self->reg20 & MASK_VIBRATO ) { + self->vibStrength = (Bit8u)(freq >> 7); + +#ifdef WAVE_PRECISION + self->vibrato = ( self->vibStrength * self->freqMul ) >> block; +#else + self->vibrato = ( self->vibStrength << block ) * self->freqMul; +#endif + } else { + self->vibStrength = 0; + self->vibrato = 0; + } +} + +static void Operator__UpdateRates(Operator *self, const Chip* chip ) { + //Mame seems to reverse this where enabling ksr actually lowers + //the rate, but pdf manuals says otherwise? + Bit8u newKsr = (Bit8u)((self->chanData >> SHIFT_KEYCODE) & 0xff); + if ( !( self->reg20 & MASK_KSR ) ) { + newKsr >>= 2; + } + if ( self->ksr == newKsr ) + return; + self->ksr = newKsr; + Operator__UpdateAttack( self, chip ); + Operator__UpdateDecay( self, chip ); + Operator__UpdateRelease( self, chip ); +} + +static inline Bit32s Operator__RateForward(Operator *self, Bit32u add ) { + Bit32s ret; + self->rateIndex += add; + /*Bit32s*/ ret = self->rateIndex >> RATE_SH; + self->rateIndex = self->rateIndex & RATE_MASK; + return ret; +} + +static Bits Operator__TemplateVolume(Operator *self, OperatorState yes) { + Bit32s vol = self->volume; + Bit32s change; + switch ( yes ) { + case OFF: + return ENV_MAX; + case ATTACK: + change = Operator__RateForward( self, self->attackAdd ); + if ( !change ) + return vol; + vol += ( (~vol) * change ) >> 3; + if ( vol < ENV_MIN ) { + self->volume = ENV_MIN; + self->rateIndex = 0; + Operator__SetState( self, DECAY ); + return ENV_MIN; + } + break; + case DECAY: + vol += Operator__RateForward( self, self->decayAdd ); + if ( GCC_UNLIKELY(vol >= self->sustainLevel) ) { + //Check if we didn't overshoot max attenuation, then just go off + if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { + self->volume = ENV_MAX; + Operator__SetState( self, OFF ); + return ENV_MAX; + } + //Continue as sustain + self->rateIndex = 0; + Operator__SetState( self, SUSTAIN ); + } + break; + case SUSTAIN: + if ( self->reg20 & MASK_SUSTAIN ) { + return vol; + } + // fallthrough + //In sustain phase, but not sustaining, do regular release + case RELEASE: + vol += Operator__RateForward( self, self->releaseAdd );; + if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { + self->volume = ENV_MAX; + Operator__SetState( self, OFF ); + return ENV_MAX; + } + break; + } + self->volume = vol; + return vol; +} + +#define TEMPLATE_VOLUME(mode) \ + static Bits Operator__TemplateVolume ## mode(Operator *self) \ + { \ + return Operator__TemplateVolume(self, mode); \ + } + +TEMPLATE_VOLUME(OFF) +TEMPLATE_VOLUME(RELEASE) +TEMPLATE_VOLUME(SUSTAIN) +TEMPLATE_VOLUME(ATTACK) +TEMPLATE_VOLUME(DECAY) + +static const VolumeHandler VolumeHandlerTable[5] = { + &Operator__TemplateVolumeOFF, + &Operator__TemplateVolumeRELEASE, + &Operator__TemplateVolumeSUSTAIN, + &Operator__TemplateVolumeDECAY, + &Operator__TemplateVolumeATTACK, +}; + +static inline Bitu Operator__ForwardVolume(Operator *self) { + return self->currentLevel + (self->volHandler)(self); +} + + +static inline Bitu Operator__ForwardWave(Operator *self) { + self->waveIndex += self->waveCurrent; + return self->waveIndex >> WAVE_SH; +} + +static void Operator__Write20(Operator *self, const Chip* chip, Bit8u val ) { + Bit8u change = (self->reg20 ^ val ); + if ( !change ) + return; + self->reg20 = val; + //Shift the tremolo bit over the entire register, saved a branch, YES! + self->tremoloMask = (Bit8s)(val) >> 7; + self->tremoloMask &= ~(( 1 << ENV_EXTRA ) -1); + //Update specific features based on changes + if ( change & MASK_KSR ) { + Operator__UpdateRates( self, chip ); + } + //With sustain enable the volume doesn't change + if ( self->reg20 & MASK_SUSTAIN || ( !self->releaseAdd ) ) { + self->rateZero |= ( 1 << SUSTAIN ); + } else { + self->rateZero &= ~( 1 << SUSTAIN ); + } + //Frequency multiplier or vibrato changed + if ( change & (0xf | MASK_VIBRATO) ) { + self->freqMul = chip->freqMul[ val & 0xf ]; + Operator__UpdateFrequency(self); + } +} + +static void Operator__Write40(Operator *self, const Chip *chip, Bit8u val ) { + if (!(self->reg40 ^ val )) + return; + self->reg40 = val; + Operator__UpdateAttenuation( self ); +} + +static void Operator__Write60(Operator *self, const Chip* chip, Bit8u val ) { + Bit8u change = self->reg60 ^ val; + self->reg60 = val; + if ( change & 0x0f ) { + Operator__UpdateDecay( self, chip ); + } + if ( change & 0xf0 ) { + Operator__UpdateAttack( self, chip ); + } +} + +static void Operator__Write80(Operator *self, const Chip* chip, Bit8u val ) { + Bit8u change = (self->reg80 ^ val ); + Bit8u sustain; + if ( !change ) + return; + self->reg80 = val; + sustain = val >> 4; + //Turn 0xf into 0x1f + sustain |= ( sustain + 1) & 0x10; + self->sustainLevel = sustain << ( ENV_BITS - 5 ); + if ( change & 0x0f ) { + Operator__UpdateRelease( self, chip ); + } +} + +static void Operator__WriteE0(Operator *self, const Chip* chip, Bit8u val ) { + Bit8u waveForm; + if ( !(self->regE0 ^ val) ) + return; + //in opl3 mode you can always selet 7 waveforms regardless of waveformselect + waveForm = val & ( ( 0x3 & chip->waveFormMask ) | (0x7 & chip->opl3Active ) ); + self->regE0 = val; +#if( DBOPL_WAVE == WAVE_HANDLER ) + self->waveHandler = WaveHandlerTable[ waveForm ]; +#else + self->waveBase = WaveTable + WaveBaseTable[ waveForm ]; + self->waveStart = WaveStartTable[ waveForm ] << WAVE_SH; + self->waveMask = WaveMaskTable[ waveForm ]; +#endif +} + +static inline void Operator__SetState(Operator *self, Bit8u s ) { + self->state = s; + self->volHandler = VolumeHandlerTable[ s ]; +} + +static inline int Operator__Silent(Operator *self) { + if ( !ENV_SILENT( self->totalLevel + self->volume ) ) + return FALSE; + if ( !(self->rateZero & ( 1 << self->state ) ) ) + return FALSE; + return TRUE; +} + +static inline void Operator__Prepare(Operator *self, const Chip* chip ) { + self->currentLevel = self->totalLevel + (chip->tremoloValue & self->tremoloMask); + self->waveCurrent = self->waveAdd; + if ( self->vibStrength >> chip->vibratoShift ) { + Bit32s add = self->vibrato >> chip->vibratoShift; + //Sign extend over the shift value + Bit32s neg = chip->vibratoSign; + //Negate the add with -1 or 0 + add = ( add ^ neg ) - neg; + self->waveCurrent += add; + } +} + +static void Operator__KeyOn(Operator *self, Bit8u mask ) { + if ( !self->keyOn ) { + //Restart the frequency generator +#if( DBOPL_WAVE > WAVE_HANDLER ) + self->waveIndex = self->waveStart; +#else + self->waveIndex = 0; +#endif + self->rateIndex = 0; + Operator__SetState( self, ATTACK ); + } + self->keyOn |= mask; +} + +static void Operator__KeyOff(Operator *self, Bit8u mask ) { + self->keyOn &= ~mask; + if ( !self->keyOn ) { + if ( self->state != OFF ) { + Operator__SetState( self, RELEASE ); + } + } +} + +static inline Bits Operator__GetWave(Operator *self, Bitu index, Bitu vol ) { +#if( DBOPL_WAVE == WAVE_HANDLER ) + return self->waveHandler( index, vol << ( 3 - ENV_EXTRA ) ); +#elif( DBOPL_WAVE == WAVE_TABLEMUL ) + return(self->waveBase[ index & self->waveMask ] * MulTable[ vol >> ENV_EXTRA ]) >> MUL_SH; +#elif( DBOPL_WAVE == WAVE_TABLELOG ) + Bit32s wave = self->waveBase[ index & self->waveMask ]; + Bit32u total = ( wave & 0x7fff ) + vol << ( 3 - ENV_EXTRA ); + Bit32s sig = ExpTable[ total & 0xff ]; + Bit32u exp = total >> 8; + Bit32s neg = wave >> 16; + return((sig ^ neg) - neg) >> exp; +#else +#error "No valid wave routine" +#endif +} + +static inline Bits Operator__GetSample(Operator *self, Bits modulation ) { + Bitu vol = Operator__ForwardVolume(self); + if ( ENV_SILENT( vol ) ) { + //Simply forward the wave + self->waveIndex += self->waveCurrent; + return 0; + } else { + Bitu index = Operator__ForwardWave(self); + index += modulation; + return Operator__GetWave( self, index, vol ); + } +} + +static void Operator__Operator(Operator *self) { + self->chanData = 0; + self->freqMul = 0; + self->waveIndex = 0; + self->waveAdd = 0; + self->waveCurrent = 0; + self->keyOn = 0; + self->ksr = 0; + self->reg20 = 0; + self->reg40 = 0; + self->reg60 = 0; + self->reg80 = 0; + self->regE0 = 0; + Operator__SetState( self, OFF ); + self->rateZero = (1 << OFF); + self->sustainLevel = ENV_MAX; + self->currentLevel = ENV_MAX; + self->totalLevel = ENV_MAX; + self->volume = ENV_MAX; + self->releaseAdd = 0; +} + +/* + Channel +*/ + +static void Channel__Channel(Channel *self) { + Operator__Operator(&self->op[0]); + Operator__Operator(&self->op[1]); + self->old[0] = self->old[1] = 0; + self->chanData = 0; + self->regB0 = 0; + self->regC0 = 0; + self->maskLeft = -1; + self->maskRight = -1; + self->feedback = 31; + self->fourMask = 0; + self->synthHandler = Channel__BlockTemplate_sm2FM; +}; + +static inline Operator* Channel__Op( Channel *self, Bitu index ) { + return &( ( self + (index >> 1) )->op[ index & 1 ]); +} + +static void Channel__SetChanData(Channel *self, const Chip* chip, Bit32u data ) { + Bit32u change = self->chanData ^ data; + self->chanData = data; + Channel__Op( self, 0 )->chanData = data; + Channel__Op( self, 1 )->chanData = data; + //Since a frequency update triggered this, always update frequency + Operator__UpdateFrequency(Channel__Op( self, 0 )); + Operator__UpdateFrequency(Channel__Op( self, 1 )); + if ( change & ( 0xff << SHIFT_KSLBASE ) ) { + Operator__UpdateAttenuation(Channel__Op( self, 0 )); + Operator__UpdateAttenuation(Channel__Op( self, 1 )); + } + if ( change & ( 0xff << SHIFT_KEYCODE ) ) { + Operator__UpdateRates(Channel__Op( self, 0 ), chip); + Operator__UpdateRates(Channel__Op( self, 1 ), chip); + } +} + +static void Channel__UpdateFrequency(Channel *self, const Chip* chip, Bit8u fourOp ) { + //Extrace the frequency bits + Bit32u data = self->chanData & 0xffff; + Bit32u kslBase = KslTable[ data >> 6 ]; + Bit32u keyCode = ( data & 0x1c00) >> 9; + if ( chip->reg08 & 0x40 ) { + keyCode |= ( data & 0x100)>>8; /* notesel == 1 */ + } else { + keyCode |= ( data & 0x200)>>9; /* notesel == 0 */ + } + //Add the keycode and ksl into the highest bits of chanData + data |= (keyCode << SHIFT_KEYCODE) | ( kslBase << SHIFT_KSLBASE ); + Channel__SetChanData( self + 0, chip, data ); + if ( fourOp & 0x3f ) { + Channel__SetChanData( self + 1, chip, data ); + } +} + +static void Channel__WriteA0(Channel *self, const Chip* chip, Bit8u val ) { + Bit32u change; + Bit8u fourOp = chip->reg104 & chip->opl3Active & self->fourMask; + //Don't handle writes to silent fourop channels + if ( fourOp > 0x80 ) + return; + change = (self->chanData ^ val ) & 0xff; + if ( change ) { + self->chanData ^= change; + Channel__UpdateFrequency( self, chip, fourOp ); + } +} + +static void Channel__WriteB0(Channel *self, const Chip* chip, Bit8u val ) { + Bitu change; + Bit8u fourOp = chip->reg104 & chip->opl3Active & self->fourMask; + //Don't handle writes to silent fourop channels + if ( fourOp > 0x80 ) + return; + change = (self->chanData ^ ( val << 8 ) ) & 0x1f00; + if ( change ) { + self->chanData ^= change; + Channel__UpdateFrequency( self, chip, fourOp ); + } + //Check for a change in the keyon/off state + if ( !(( val ^ self->regB0) & 0x20)) + return; + self->regB0 = val; + if ( val & 0x20 ) { + Operator__KeyOn( Channel__Op(self, 0), 0x1 ); + Operator__KeyOn( Channel__Op(self, 1), 0x1 ); + if ( fourOp & 0x3f ) { + Operator__KeyOn( Channel__Op(self + 1, 0), 1 ); + Operator__KeyOn( Channel__Op(self + 1, 1), 1 ); + } + } else { + Operator__KeyOff( Channel__Op(self, 0), 0x1 ); + Operator__KeyOff( Channel__Op(self, 1), 0x1 ); + if ( fourOp & 0x3f ) { + Operator__KeyOff( Channel__Op(self + 1, 0), 1 ); + Operator__KeyOff( Channel__Op(self + 1, 1), 1 ); + } + } +} + +static void Channel__WriteC0(Channel *self, const Chip* chip, Bit8u val ) { + Bit8u synth; + Bit8u change = val ^ self->regC0; + if ( !change ) + return; + self->regC0 = val; + self->feedback = ( val >> 1 ) & 7; + if ( self->feedback ) { + //We shift the input to the right 10 bit wave index value + self->feedback = 9 - self->feedback; + } else { + self->feedback = 31; + } + //Select the new synth mode + if ( chip->opl3Active ) { + //4-op mode enabled for this channel + if ( (chip->reg104 & self->fourMask) & 0x3f ) { + Channel* chan0, *chan1; + //Check if it's the 2nd channel in a 4-op + if ( !(self->fourMask & 0x80 ) ) { + chan0 = self; + chan1 = self + 1; + } else { + chan0 = self - 1; + chan1 = self; + } + + synth = ( (chan0->regC0 & 1) << 0 )| (( chan1->regC0 & 1) << 1 ); + switch ( synth ) { + case 0: + chan0->synthHandler = Channel__BlockTemplate_sm3FMFM; + break; + case 1: + chan0->synthHandler = Channel__BlockTemplate_sm3AMFM; + break; + case 2: + chan0->synthHandler = Channel__BlockTemplate_sm3FMAM ; + break; + case 3: + chan0->synthHandler = Channel__BlockTemplate_sm3AMAM ; + break; + } + //Disable updating percussion channels + } else if ((self->fourMask & 0x40) && ( chip->regBD & 0x20) ) { + + //Regular dual op, am or fm + } else if ( val & 1 ) { + self->synthHandler = Channel__BlockTemplate_sm3AM; + } else { + self->synthHandler = Channel__BlockTemplate_sm3FM; + } + self->maskLeft = ( val & 0x10 ) ? -1 : 0; + self->maskRight = ( val & 0x20 ) ? -1 : 0; + //opl2 active + } else { + //Disable updating percussion channels + if ( (self->fourMask & 0x40) && ( chip->regBD & 0x20 ) ) { + + //Regular dual op, am or fm + } else if ( val & 1 ) { + self->synthHandler = Channel__BlockTemplate_sm2AM; + } else { + self->synthHandler = Channel__BlockTemplate_sm2FM; + } + } +} + +static void Channel__ResetC0(Channel *self, const Chip* chip ) { + Bit8u val = self->regC0; + self->regC0 ^= 0xff; + Channel__WriteC0( self, chip, val ); +}; + +static inline void Channel__GeneratePercussion(Channel *self, Chip* chip, + Bit32s* output, int opl3Mode ) { + Bit32u noiseBit, c2, c5, phaseBit, hhVol, sdVol, tcVol; + Bit32s sample; + Channel* chan = self; + + //BassDrum + Bit32s mod = (Bit32u)((self->old[0] + self->old[1])) >> self->feedback; + self->old[0] = self->old[1]; + self->old[1] = Operator__GetSample( Channel__Op(self, 0), mod ); + + //When bassdrum is in AM mode first operator is ignoed + if ( chan->regC0 & 1 ) { + mod = 0; + } else { + mod = self->old[0]; + } + sample = Operator__GetSample( Channel__Op(self, 1), mod ); + + //Precalculate stuff used by other outputs + noiseBit = Chip__ForwardNoise(chip) & 0x1; + c2 = Operator__ForwardWave(Channel__Op(self, 2)); + c5 = Operator__ForwardWave(Channel__Op(self, 5)); + phaseBit = (((c2 & 0x88) ^ ((c2<<5) & 0x80)) | ((c5 ^ (c5<<2)) & 0x20)) ? 0x02 : 0x00; + + //Hi-Hat + hhVol = Operator__ForwardVolume(Channel__Op(self, 2)); + if ( !ENV_SILENT( hhVol ) ) { + Bit32u hhIndex = (phaseBit<<8) | (0x34 << ( phaseBit ^ (noiseBit << 1 ))); + sample += Operator__GetWave( Channel__Op(self, 2), hhIndex, hhVol ); + } + //Snare Drum + sdVol = Operator__ForwardVolume( Channel__Op(self, 3) ); + if ( !ENV_SILENT( sdVol ) ) { + Bit32u sdIndex = ( 0x100 + (c2 & 0x100) ) ^ ( noiseBit << 8 ); + sample += Operator__GetWave( Channel__Op(self, 3), sdIndex, sdVol ); + } + //Tom-tom + sample += Operator__GetSample( Channel__Op(self, 4), 0 ); + + //Top-Cymbal + tcVol = Operator__ForwardVolume(Channel__Op(self, 5)); + if ( !ENV_SILENT( tcVol ) ) { + Bit32u tcIndex = (1 + phaseBit) << 8; + sample += Operator__GetWave( Channel__Op(self, 5), tcIndex, tcVol ); + } + sample <<= 1; + if ( opl3Mode ) { + output[0] += sample; + output[1] += sample; + } else { + output[0] += sample; + } +} + +Channel* Channel__BlockTemplate(Channel *self, Chip* chip, + Bit32u samples, Bit32s* output, + SynthMode mode ) { + Bitu i; + + switch( mode ) { + case sm2AM: + case sm3AM: + if ( Operator__Silent(Channel__Op(self, 0)) + && Operator__Silent(Channel__Op(self, 1))) { + self->old[0] = self->old[1] = 0; + return(self + 1); + } + break; + case sm2FM: + case sm3FM: + if ( Operator__Silent(Channel__Op(self, 1))) { + self->old[0] = self->old[1] = 0; + return (self + 1); + } + break; + case sm3FMFM: + if ( Operator__Silent(Channel__Op(self, 3))) { + self->old[0] = self->old[1] = 0; + return (self + 2); + } + break; + case sm3AMFM: + if ( Operator__Silent( Channel__Op(self, 0) ) + && Operator__Silent( Channel__Op(self, 3) )) { + self->old[0] = self->old[1] = 0; + return (self + 2); + } + break; + case sm3FMAM: + if ( Operator__Silent( Channel__Op(self, 1)) + && Operator__Silent( Channel__Op(self, 3))) { + self->old[0] = self->old[1] = 0; + return (self + 2); + } + break; + case sm3AMAM: + if ( Operator__Silent( Channel__Op(self, 0) ) + && Operator__Silent( Channel__Op(self, 2) ) + && Operator__Silent( Channel__Op(self, 3) )) { + self->old[0] = self->old[1] = 0; + return (self + 2); + } + break; + + default: + abort(); + } + //Init the operators with the the current vibrato and tremolo values + Operator__Prepare( Channel__Op( self, 0 ), chip ); + Operator__Prepare( Channel__Op( self, 1 ), chip ); + if ( mode > sm4Start ) { + Operator__Prepare( Channel__Op( self, 2 ), chip ); + Operator__Prepare( Channel__Op( self, 3 ), chip ); + } + if ( mode > sm6Start ) { + Operator__Prepare( Channel__Op( self, 4 ), chip ); + Operator__Prepare( Channel__Op( self, 5 ), chip ); + } + for ( i = 0; i < samples; i++ ) { + Bit32s mod, sample, out0; + Bits next; + //Early out for percussion handlers + if ( mode == sm2Percussion ) { + Channel__GeneratePercussion( self, chip, output + i, FALSE ); + continue; //Prevent some unitialized value bitching + } else if ( mode == sm3Percussion ) { + Channel__GeneratePercussion( self, chip, output + i * 2, TRUE ); + continue; //Prevent some unitialized value bitching + } + + //Do unsigned shift so we can shift out all bits but still stay in 10 bit range otherwise + mod = (Bit32u)((self->old[0] + self->old[1])) >> self->feedback; + self->old[0] = self->old[1]; + self->old[1] = Operator__GetSample( Channel__Op(self, 0), mod ); + sample = 0; + out0 = self->old[0]; + if ( mode == sm2AM || mode == sm3AM ) { + sample = out0 + Operator__GetSample( Channel__Op(self, 1), 0 ); + } else if ( mode == sm2FM || mode == sm3FM ) { + sample = Operator__GetSample( Channel__Op(self, 1), out0 ); + } else if ( mode == sm3FMFM ) { + next = Operator__GetSample( Channel__Op(self, 1), out0 ); + next = Operator__GetSample( Channel__Op(self, 2), next ); + sample = Operator__GetSample( Channel__Op(self, 3), next ); + } else if ( mode == sm3AMFM ) { + sample = out0; + next = Operator__GetSample( Channel__Op(self, 1), 0 ); + next = Operator__GetSample( Channel__Op(self, 2), next ); + sample += Operator__GetSample( Channel__Op(self, 3), next ); + } else if ( mode == sm3FMAM ) { + sample = Operator__GetSample( Channel__Op(self, 1), out0 ); + next = Operator__GetSample( Channel__Op(self, 2), 0 ); + sample += Operator__GetSample( Channel__Op(self, 3), next ); + } else if ( mode == sm3AMAM ) { + sample = out0; + next = Operator__GetSample( Channel__Op(self, 1), 0 ); + sample += Operator__GetSample( Channel__Op(self, 2), next ); + sample += Operator__GetSample( Channel__Op(self, 3), 0 ); + } + switch( mode ) { + case sm2AM: + case sm2FM: + output[ i ] += sample; + break; + case sm3AM: + case sm3FM: + case sm3FMFM: + case sm3AMFM: + case sm3FMAM: + case sm3AMAM: + output[ i * 2 + 0 ] += sample & self->maskLeft; + output[ i * 2 + 1 ] += sample & self->maskRight; + break; + default: + abort(); + } + } + switch( mode ) { + case sm2AM: + case sm2FM: + case sm3AM: + case sm3FM: + return ( self + 1 ); + case sm3FMFM: + case sm3AMFM: + case sm3FMAM: + case sm3AMAM: + return ( self + 2 ); + case sm2Percussion: + case sm3Percussion: + return( self + 3 ); + default: + abort(); + } + return 0; +} + +/* + Chip +*/ + +void Chip__Chip(Chip *self) { + int i; + + for (i=0; i<18; ++i) { + Channel__Channel(&self->chan[i]); + } + + self->reg08 = 0; + self->reg04 = 0; + self->regBD = 0; + self->reg104 = 0; + self->opl3Active = 0; +} + +static inline Bit32u Chip__ForwardNoise(Chip *self) { + Bitu count; + self->noiseCounter += self->noiseAdd; + count = self->noiseCounter >> LFO_SH; + self->noiseCounter &= WAVE_MASK; + for ( ; count > 0; --count ) { + //Noise calculation from mame + self->noiseValue ^= ( 0x800302 ) & ( 0 - (self->noiseValue & 1 ) ); + self->noiseValue >>= 1; + } + return self->noiseValue; +} + +static inline Bit32u Chip__ForwardLFO(Chip *self, Bit32u samples ) { + Bit32u todo, count; + //Current vibrato value, runs 4x slower than tremolo + self->vibratoSign = ( VibratoTable[ self->vibratoIndex >> 2] ) >> 7; + self->vibratoShift = ( VibratoTable[ self->vibratoIndex >> 2] & 7) + self->vibratoStrength; + self->tremoloValue = TremoloTable[ self->tremoloIndex ] >> self->tremoloStrength; + + //Check hom many samples there can be done before the value changes + todo = LFO_MAX - self->lfoCounter; + count = (todo + self->lfoAdd - 1) / self->lfoAdd; + if ( count > samples ) { + count = samples; + self->lfoCounter += count * self->lfoAdd; + } else { + self->lfoCounter += count * self->lfoAdd; + self->lfoCounter &= (LFO_MAX - 1); + //Maximum of 7 vibrato value * 4 + self->vibratoIndex = ( self->vibratoIndex + 1 ) & 31; + //Clip tremolo to the the table size + if ( self->tremoloIndex + 1 < TREMOLO_TABLE ) + ++self->tremoloIndex; + else + self->tremoloIndex = 0; + } + return count; +} + + +static void Chip__WriteBD(Chip *self, Bit8u val ) { + Bit8u change = self->regBD ^ val; + if ( !change ) + return; + self->regBD = val; + //TODO could do this with shift and xor? + self->vibratoStrength = (val & 0x40) ? 0x00 : 0x01; + self->tremoloStrength = (val & 0x80) ? 0x00 : 0x02; + if ( val & 0x20 ) { + //Drum was just enabled, make sure channel 6 has the right synth + if ( change & 0x20 ) { + if ( self->opl3Active ) { + self->chan[6].synthHandler + = Channel__BlockTemplate_sm3Percussion; + } else { + self->chan[6].synthHandler + = Channel__BlockTemplate_sm2Percussion; + } + } + //Bass Drum + if ( val & 0x10 ) { + Operator__KeyOn( &self->chan[6].op[0], 0x2 ); + Operator__KeyOn( &self->chan[6].op[1], 0x2 ); + } else { + Operator__KeyOff( &self->chan[6].op[0], 0x2 ); + Operator__KeyOff( &self->chan[6].op[1], 0x2 ); + } + //Hi-Hat + if ( val & 0x1 ) { + Operator__KeyOn( &self->chan[7].op[0], 0x2 ); + } else { + Operator__KeyOff( &self->chan[7].op[0], 0x2 ); + } + //Snare + if ( val & 0x8 ) { + Operator__KeyOn( &self->chan[7].op[1], 0x2 ); + } else { + Operator__KeyOff( &self->chan[7].op[1], 0x2 ); + } + //Tom-Tom + if ( val & 0x4 ) { + Operator__KeyOn( &self->chan[8].op[0], 0x2 ); + } else { + Operator__KeyOff( &self->chan[8].op[0], 0x2 ); + } + //Top Cymbal + if ( val & 0x2 ) { + Operator__KeyOn( &self->chan[8].op[1], 0x2 ); + } else { + Operator__KeyOff( &self->chan[8].op[1], 0x2 ); + } + //Toggle keyoffs when we turn off the percussion + } else if ( change & 0x20 ) { + //Trigger a reset to setup the original synth handler + Channel__ResetC0( &self->chan[6], self ); + Operator__KeyOff( &self->chan[6].op[0], 0x2 ); + Operator__KeyOff( &self->chan[6].op[1], 0x2 ); + Operator__KeyOff( &self->chan[7].op[0], 0x2 ); + Operator__KeyOff( &self->chan[7].op[1], 0x2 ); + Operator__KeyOff( &self->chan[8].op[0], 0x2 ); + Operator__KeyOff( &self->chan[8].op[1], 0x2 ); + } +} + + +#define REGOP( _FUNC_ ) \ + index = ( ( reg >> 3) & 0x20 ) | ( reg & 0x1f ); \ + if ( OpOffsetTable[ index ] ) { \ + Operator* regOp = (Operator*)( ((char *)self ) + OpOffsetTable[ index ] ); \ + Operator__ ## _FUNC_ (regOp, self, val); \ + } + +#define REGCHAN( _FUNC_ ) \ + index = ( ( reg >> 4) & 0x10 ) | ( reg & 0xf ); \ + if ( ChanOffsetTable[ index ] ) { \ + Channel* regChan = (Channel*)( ((char *)self ) + ChanOffsetTable[ index ] ); \ + Channel__ ## _FUNC_ (regChan, self, val); \ + } + +void Chip__WriteReg(Chip *self, Bit32u reg, Bit8u val ) { + Bitu index; + switch ( (reg & 0xf0) >> 4 ) { + case 0x00 >> 4: + if ( reg == 0x01 ) { + self->waveFormMask = ( val & 0x20 ) ? 0x7 : 0x0; + } else if ( reg == 0x104 ) { + //Only detect changes in lowest 6 bits + if ( !((self->reg104 ^ val) & 0x3f) ) + return; + //Always keep the highest bit enabled, for checking > 0x80 + self->reg104 = 0x80 | ( val & 0x3f ); + } else if ( reg == 0x105 ) { + int i; + + //MAME says the real opl3 doesn't reset anything on opl3 disable/enable till the next write in another register + if ( !((self->opl3Active ^ val) & 1 ) ) + return; + self->opl3Active = ( val & 1 ) ? 0xff : 0; + //Update the 0xc0 register for all channels to signal the switch to mono/stereo handlers + for ( i = 0; i < 18;i++ ) { + Channel__ResetC0( &self->chan[i], self ); + } + } else if ( reg == 0x08 ) { + self->reg08 = val; + } + case 0x10 >> 4: + break; + case 0x20 >> 4: + case 0x30 >> 4: + REGOP( Write20 ); + break; + case 0x40 >> 4: + case 0x50 >> 4: + REGOP( Write40 ); + break; + case 0x60 >> 4: + case 0x70 >> 4: + REGOP( Write60 ); + break; + case 0x80 >> 4: + case 0x90 >> 4: + REGOP( Write80 ); + break; + case 0xa0 >> 4: + REGCHAN( WriteA0 ); + break; + case 0xb0 >> 4: + if ( reg == 0xbd ) { + Chip__WriteBD( self, val ); + } else { + REGCHAN( WriteB0 ); + } + break; + case 0xc0 >> 4: + REGCHAN( WriteC0 ); + case 0xd0 >> 4: + break; + case 0xe0 >> 4: + case 0xf0 >> 4: + REGOP( WriteE0 ); + break; + } +} + +Bit32u Chip__WriteAddr(Chip *self, Bit32u port, Bit8u val ) { + switch ( port & 3 ) { + case 0: + return val; + case 2: + if ( self->opl3Active || (val == 0x05) ) + return 0x100 | val; + else + return val; + } + return 0; +} + +void Chip__GenerateBlock2(Chip *self, Bitu total, Bit32s* output ) { + while ( total > 0 ) { + Channel *ch; + int count; + + Bit32u samples = Chip__ForwardLFO( self, total ); + memset(output, 0, sizeof(Bit32s) * samples); + count = 0; + for ( ch = self->chan; ch < self->chan + 9; ) { + count++; + ch = (ch->synthHandler)( ch, self, samples, output ); + } + total -= samples; + output += samples; + } +} + +void Chip__GenerateBlock3(Chip *self, Bitu total, Bit32s* output ) { + while ( total > 0 ) { + int count; + Channel *ch; + + Bit32u samples = Chip__ForwardLFO( self, total ); + memset(output, 0, sizeof(Bit32s) * samples *2); + count = 0; + for ( ch = self->chan; ch < self->chan + 18; ) { + count++; + ch = (ch->synthHandler)( ch, self, samples, output ); + } + total -= samples; + output += samples * 2; + } +} + +void Chip__Setup(Chip *self, Bit32u rate ) { + double original = OPLRATE; + Bit32u i; +// double original = rate; + double scale = original / (double)rate; + + //Noise counter is run at the same precision as general waves + self->noiseAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); + self->noiseCounter = 0; + self->noiseValue = 1; //Make sure it triggers the noise xor the first time + //The low frequency oscillation counter + //Every time his overflows vibrato and tremoloindex are increased + self->lfoAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); + self->lfoCounter = 0; + self->vibratoIndex = 0; + self->tremoloIndex = 0; + + //With higher octave this gets shifted up + //-1 since the freqCreateTable = *2 + { +#ifdef WAVE_PRECISION + double freqScale = ( 1 << 7 ) * scale * ( 1 << ( WAVE_SH - 1 - 10)); + for ( i = 0; i < 16; i++ ) { + self->freqMul[i] = (Bit32u)( 0.5 + freqScale * FreqCreateTable[ i ] ); + } +#else + Bit32u freqScale = (Bit32u)( 0.5 + scale * ( 1 << ( WAVE_SH - 1 - 10))); + for ( i = 0; i < 16; i++ ) { + self->freqMul[i] = freqScale * FreqCreateTable[ i ]; + } +#endif + } + + //-3 since the real envelope takes 8 steps to reach the single value we supply + for ( i = 0; i < 76; i++ ) { + Bit8u index, shift; + EnvelopeSelect( (Bit8u) i, &index, &shift ); + self->linearRates[i] = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH + ENV_EXTRA - shift - 3 ))); + } + //Generate the best matching attack rate + for ( i = 0; i < 62; i++ ) { + Bit8u index, shift; + Bit32s original, guessAdd, bestAdd, bestDiff; + Bit32u passes; + EnvelopeSelect( (Bit8u) i, &index, &shift ); + //Original amount of samples the attack would take + original = (Bit32u)( (AttackSamplesTable[ index ] << shift) / scale); + + guessAdd = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH - shift - 3 ))); + bestAdd = guessAdd; + bestDiff = 1 << 30; + + for ( passes = 0; passes < 16; passes ++ ) { + Bit32s diff, lDiff; + Bit32s volume = ENV_MAX; + Bit32s samples = 0; + Bit32u count = 0; + while ( volume > 0 && samples < original * 2 ) { + Bit32s change; + count += guessAdd; + change = count >> RATE_SH; + count &= RATE_MASK; + if ( GCC_UNLIKELY(change) ) { // less than 1 % + volume += ( ~volume * change ) >> 3; + } + samples++; + + } + diff = original - samples; + lDiff = labs( diff ); + //Init last on first pass + if ( lDiff < bestDiff ) { + bestDiff = lDiff; + bestAdd = guessAdd; + if ( !bestDiff ) + break; + } + //Below our target + if ( diff < 0 ) { + //Better than the last time + Bit32s mul = ((original - diff) << 12) / original; + guessAdd = ((guessAdd * mul) >> 12); + guessAdd++; + } else if ( diff > 0 ) { + Bit32s mul = ((original - diff) << 12) / original; + guessAdd = (guessAdd * mul) >> 12; + guessAdd--; + } + } + self->attackRates[i] = bestAdd; + } + for ( i = 62; i < 76; i++ ) { + //This should provide instant volume maximizing + self->attackRates[i] = 8 << RATE_SH; + } + //Setup the channels with the correct four op flags + //Channels are accessed through a table so they appear linear here + self->chan[ 0].fourMask = 0x00 | ( 1 << 0 ); + self->chan[ 1].fourMask = 0x80 | ( 1 << 0 ); + self->chan[ 2].fourMask = 0x00 | ( 1 << 1 ); + self->chan[ 3].fourMask = 0x80 | ( 1 << 1 ); + self->chan[ 4].fourMask = 0x00 | ( 1 << 2 ); + self->chan[ 5].fourMask = 0x80 | ( 1 << 2 ); + + self->chan[ 9].fourMask = 0x00 | ( 1 << 3 ); + self->chan[10].fourMask = 0x80 | ( 1 << 3 ); + self->chan[11].fourMask = 0x00 | ( 1 << 4 ); + self->chan[12].fourMask = 0x80 | ( 1 << 4 ); + self->chan[13].fourMask = 0x00 | ( 1 << 5 ); + self->chan[14].fourMask = 0x80 | ( 1 << 5 ); + + //mark the percussion channels + self->chan[ 6].fourMask = 0x40; + self->chan[ 7].fourMask = 0x40; + self->chan[ 8].fourMask = 0x40; + + //Clear Everything in opl3 mode + Chip__WriteReg( self, 0x105, 0x1 ); + for ( i = 0; i < 512; i++ ) { + if ( i == 0x105 ) + continue; + Chip__WriteReg( self, i, 0xff ); + Chip__WriteReg( self, i, 0x0 ); + } + Chip__WriteReg( self, 0x105, 0x0 ); + //Clear everything in opl2 mode + for ( i = 0; i < 255; i++ ) { + Chip__WriteReg( self, i, 0xff ); + Chip__WriteReg( self, i, 0x0 ); + } +} + +static int doneTables = FALSE; +void DBOPL_InitTables( void ) { + int i, oct; + Chip *chip = NULL; + + if ( doneTables ) + return; + doneTables = TRUE; +#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) + //Exponential volume table, same as the real adlib + for ( i = 0; i < 256; i++ ) { + //Save them in reverse + ExpTable[i] = (int)( 0.5 + ( pow(2.0, ( 255 - i) * ( 1.0 /256 ) )-1) * 1024 ); + ExpTable[i] += 1024; //or remove the -1 oh well :) + //Preshift to the left once so the final volume can shift to the right + ExpTable[i] *= 2; + } +#endif +#if ( DBOPL_WAVE == WAVE_HANDLER ) + //Add 0.5 for the trunc rounding of the integer cast + //Do a PI sinetable instead of the original 0.5 PI + for ( i = 0; i < 512; i++ ) { + SinTable[i] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); + } +#endif +#if ( DBOPL_WAVE == WAVE_TABLEMUL ) + //Multiplication based tables + for ( i = 0; i < 384; i++ ) { + int s = i * 8; + //TODO maybe keep some of the precision errors of the original table? + double val = ( 0.5 + ( pow(2.0, -1.0 + ( 255 - s) * ( 1.0 /256 ) )) * ( 1 << MUL_SH )); + MulTable[i] = (Bit16u)(val); + } + + //Sine Wave Base + for ( i = 0; i < 512; i++ ) { + WaveTable[ 0x0200 + i ] = (Bit16s)(sin( (i + 0.5) * (PI / 512.0) ) * 4084); + WaveTable[ 0x0000 + i ] = -WaveTable[ 0x200 + i ]; + } + //Exponential wave + for ( i = 0; i < 256; i++ ) { + WaveTable[ 0x700 + i ] = (Bit16s)( 0.5 + ( pow(2.0, -1.0 + ( 255 - i * 8) * ( 1.0 /256 ) ) ) * 4085 ); + WaveTable[ 0x6ff - i ] = -WaveTable[ 0x700 + i ]; + } +#endif +#if ( DBOPL_WAVE == WAVE_TABLELOG ) + //Sine Wave Base + for ( i = 0; i < 512; i++ ) { + WaveTable[ 0x0200 + i ] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); + WaveTable[ 0x0000 + i ] = ((Bit16s)0x8000) | WaveTable[ 0x200 + i]; + } + //Exponential wave + for ( i = 0; i < 256; i++ ) { + WaveTable[ 0x700 + i ] = i * 8; + WaveTable[ 0x6ff - i ] = ((Bit16s)0x8000) | i * 8; + } +#endif + + // | |//\\|____|WAV7|//__|/\ |____|/\/\| + // |\\//| | |WAV7| | \/| | | + // |06 |0126|27 |7 |3 |4 |4 5 |5 | + +#if (( DBOPL_WAVE == WAVE_TABLELOG ) || ( DBOPL_WAVE == WAVE_TABLEMUL )) + for ( i = 0; i < 256; i++ ) { + //Fill silence gaps + WaveTable[ 0x400 + i ] = WaveTable[0]; + WaveTable[ 0x500 + i ] = WaveTable[0]; + WaveTable[ 0x900 + i ] = WaveTable[0]; + WaveTable[ 0xc00 + i ] = WaveTable[0]; + WaveTable[ 0xd00 + i ] = WaveTable[0]; + //Replicate sines in other pieces + WaveTable[ 0x800 + i ] = WaveTable[ 0x200 + i ]; + //double speed sines + WaveTable[ 0xa00 + i ] = WaveTable[ 0x200 + i * 2 ]; + WaveTable[ 0xb00 + i ] = WaveTable[ 0x000 + i * 2 ]; + WaveTable[ 0xe00 + i ] = WaveTable[ 0x200 + i * 2 ]; + WaveTable[ 0xf00 + i ] = WaveTable[ 0x200 + i * 2 ]; + } +#endif + + //Create the ksl table + for ( oct = 0; oct < 8; oct++ ) { + int base = oct * 8; + for ( i = 0; i < 16; i++ ) { + int val = base - KslCreateTable[i]; + if ( val < 0 ) + val = 0; + //*4 for the final range to match attenuation range + KslTable[ oct * 16 + i ] = val * 4; + } + } + //Create the Tremolo table, just increase and decrease a triangle wave + for ( i = 0; i < TREMOLO_TABLE / 2; i++ ) { + Bit8u val = i << ENV_EXTRA; + TremoloTable[i] = val; + TremoloTable[TREMOLO_TABLE - 1 - i] = val; + } + //Create a table with offsets of the channels from the start of the chip + for ( i = 0; i < 32; i++ ) { + Bitu blah; + Bitu index = i & 0xf; + if ( index >= 9 ) { + ChanOffsetTable[i] = 0; + continue; + } + //Make sure the four op channels follow eachother + if ( index < 6 ) { + index = (index % 3) * 2 + ( index / 3 ); + } + //Add back the bits for highest ones + if ( i >= 16 ) + index += 9; + blah = (Bitu) ( &(chip->chan[ index ]) ); + ChanOffsetTable[i] = (Bit16u) blah; + } + //Same for operators + for ( i = 0; i < 64; i++ ) { + Bitu chNum, opNum, blah; + Channel* chan = NULL; + if ( i % 8 >= 6 || ( (i / 8) % 4 == 3 ) ) { + OpOffsetTable[i] = 0; + continue; + } + chNum = (i / 8) * 3 + (i % 8) % 3; + //Make sure we use 16 and up for the 2nd range to match the chanoffset gap + if ( chNum >= 12 ) + chNum += 16 - 12; + opNum = ( i % 8 ) / 3; + blah = (Bitu) ( &(chan->op[opNum]) ); + OpOffsetTable[i] = (Bit16u) (ChanOffsetTable[ chNum ] + blah); + } +#if 0 + //Stupid checks if table's are correct + for ( Bitu i = 0; i < 18; i++ ) { + Bit32u find = (Bit16u)( &(chip->chan[ i ]) ); + for ( Bitu c = 0; c < 32; c++ ) { + if ( ChanOffsetTable[c] == find ) { + find = 0; + break; + } + } + if ( find ) { + find = find; + } + } + for ( Bitu i = 0; i < 36; i++ ) { + Bit32u find = (Bit16u)( &(chip->chan[ i / 2 ].op[i % 2]) ); + for ( Bitu c = 0; c < 64; c++ ) { + if ( OpOffsetTable[c] == find ) { + find = 0; + break; + } + } + if ( find ) { + find = find; + } + } +#endif +} + + diff --git a/src/MUSIC/dbopl.h b/src/MUSIC/dbopl.h new file mode 100644 index 0000000..2b1d44e --- /dev/null +++ b/src/MUSIC/dbopl.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2002-2010 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +//#include "inttypes.h" //NSM +#include "SDL_stdinc.h" + +#ifdef _MSC_VER + #if _MSC_VER < 1300 + #include + typedef INT_PTR intptr_t; + #endif +#endif + +//Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume +#define WAVE_HANDLER 10 +//Use a logarithmic wavetable with an exponential table for volume +#define WAVE_TABLELOG 11 +//Use a linear wavetable with a multiply table for volume +#define WAVE_TABLEMUL 12 + +//Select the type of wave generator routine +#define DBOPL_WAVE WAVE_TABLEMUL + +typedef struct _Chip Chip; +typedef struct _Operator Operator; +typedef struct _Channel Channel; + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +#if (DBOPL_WAVE == WAVE_HANDLER) +typedef Bits ( DB_FASTCALL *WaveHandler) ( Bitu i, Bitu volume ); +#endif + +#define DB_FASTCALL + +typedef Bits (*VolumeHandler)(Operator *self); +typedef Channel* (*SynthHandler)(Channel *self, Chip* chip, Bit32u samples, Bit32s* output ); + +//Different synth modes that can generate blocks of data +typedef enum { + sm2AM, + sm2FM, + sm3AM, + sm3FM, + sm4Start, + sm3FMFM, + sm3AMFM, + sm3FMAM, + sm3AMAM, + sm6Start, + sm2Percussion, + sm3Percussion, +} SynthMode; + +//Shifts for the values contained in chandata variable +enum { + SHIFT_KSLBASE = 16, + SHIFT_KEYCODE = 24, +}; + +//Masks for operator 20 values +enum { + MASK_KSR = 0x10, + MASK_SUSTAIN = 0x20, + MASK_VIBRATO = 0x40, + MASK_TREMOLO = 0x80, +}; + +typedef enum { + OFF, + RELEASE, + SUSTAIN, + DECAY, + ATTACK, +} OperatorState; + +struct _Operator { + VolumeHandler volHandler; + +#if (DBOPL_WAVE == WAVE_HANDLER) + WaveHandler waveHandler; //Routine that generate a wave +#else + Bit16s* waveBase; + Bit32u waveMask; + Bit32u waveStart; +#endif + Bit32u waveIndex; //WAVE_BITS shifted counter of the frequency index + Bit32u waveAdd; //The base frequency without vibrato + Bit32u waveCurrent; //waveAdd + vibratao + + Bit32u chanData; //Frequency/octave and derived data coming from whatever channel controls this + Bit32u freqMul; //Scale channel frequency with this, TODO maybe remove? + Bit32u vibrato; //Scaled up vibrato strength + Bit32s sustainLevel; //When stopping at sustain level stop here + Bit32s totalLevel; //totalLevel is added to every generated volume + Bit32u currentLevel; //totalLevel + tremolo + Bit32s volume; //The currently active volume + + Bit32u attackAdd; //Timers for the different states of the envelope + Bit32u decayAdd; + Bit32u releaseAdd; + Bit32u rateIndex; //Current position of the evenlope + + Bit8u rateZero; //Bits for the different states of the envelope having no changes + Bit8u keyOn; //Bitmask of different values that can generate keyon + //Registers, also used to check for changes + Bit8u reg20, reg40, reg60, reg80, regE0; + //Active part of the envelope we're in + Bit8u state; + //0xff when tremolo is enabled + Bit8u tremoloMask; + //Strength of the vibrato + Bit8u vibStrength; + //Keep track of the calculated KSR so we can check for changes + Bit8u ksr; +}; + +struct _Channel { + Operator op[2]; + SynthHandler synthHandler; + Bit32u chanData; //Frequency/octave and derived values + Bit32s old[2]; //Old data for feedback + + Bit8u feedback; //Feedback shift + Bit8u regB0; //Register values to check for changes + Bit8u regC0; + //This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel + Bit8u fourMask; + Bit8s maskLeft; //Sign extended values for both channel's panning + Bit8s maskRight; + +}; + +struct _Chip { + //This is used as the base counter for vibrato and tremolo + Bit32u lfoCounter; + Bit32u lfoAdd; + + + Bit32u noiseCounter; + Bit32u noiseAdd; + Bit32u noiseValue; + + //Frequency scales for the different multiplications + Bit32u freqMul[16]; + //Rates for decay and release for rate of this chip + Bit32u linearRates[76]; + //Best match attack rates for the rate of this chip + Bit32u attackRates[76]; + + //18 channels with 2 operators each + Channel chan[18]; + + Bit8u reg104; + Bit8u reg08; + Bit8u reg04; + Bit8u regBD; + Bit8u vibratoIndex; + Bit8u tremoloIndex; + Bit8s vibratoSign; + Bit8u vibratoShift; + Bit8u tremoloValue; + Bit8u vibratoStrength; + Bit8u tremoloStrength; + //Mask for allowed wave forms + Bit8u waveFormMask; + //0 or -1 when enabled + Bit8s opl3Active; + +}; + +/* +struct Handler : public Adlib::Handler { + DBOPL::Chip chip; + virtual Bit32u WriteAddr( Bit32u port, Bit8u val ); + virtual void WriteReg( Bit32u addr, Bit8u val ); + virtual void Generate( MixerChannel* chan, Bitu samples ); + virtual void Init( Bitu rate ); +}; +*/ + +#ifdef __cplusplus +extern "C" void Chip__Setup(Chip *self, Bit32u rate ); +extern "C" void DBOPL_InitTables( void ); +extern "C" void Chip__Chip(Chip *self); +extern "C" void Chip__WriteReg(Chip *self, Bit32u reg, Bit8u val ); +extern "C" void Chip__GenerateBlock2(Chip *self, Bitu total, Bit32s* output ); +#else +void Chip__Setup(Chip *self, Bit32u rate ); +void DBOPL_InitTables( void ); +void Chip__Chip(Chip *self); +void Chip__WriteReg(Chip *self, Bit32u reg, Bit8u val ); +void Chip__GenerateBlock2(Chip *self, Bitu total, Bit32s* output ); + +#endif + diff --git a/src/MUSIC/dumbplayer.c b/src/MUSIC/dumbplayer.c new file mode 100644 index 0000000..18c96ac --- /dev/null +++ b/src/MUSIC/dumbplayer.c @@ -0,0 +1,295 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * + * Copyright (C) 2011 by + * Nicholai Main + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "musicplayer.h" + +#ifndef HAVE_LIBDUMB +#include + +static const char *db_name (void) +{ + return "dumb tracker player (DISABLED)"; +} + + +static int db_init (int samplerate) +{ + return 0; +} + +const music_player_t db_player = +{ + db_name, + db_init, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +#else // HAVE_DUMB + +#if !defined(_FILE_OFFSET_BITS) || (_FILE_OFFSET_BITS < 64) +#ifdef _MSC_VER +#define DUMB_OFF_T_CUSTOM __int64 +#else +#include +#define DUMB_OFF_T_CUSTOM int64_t +#endif +#endif + +#include +#include +#include "lprintf.h" + + +static float db_delta; +static float db_volume; +static int db_looping; +static int db_playing = 0; +static int db_paused = 0; + +static DUH_SIGRENDERER *dsren = NULL; +static DUH *duh = NULL; +static DUMBFILE *dfil = NULL; + +static const char *db_name (void) +{ + return "dumb tracker player"; +} + + +static int db_init (int samplerate) +{ + db_delta = 65536.0f / samplerate; + + return 1; +} + +static void db_shutdown (void) +{ + dumb_exit (); +} + +static void db_setvolume (int v) +{ + db_volume = (float) v / 15.0f; +} + +static const void* db_registersong (const void *data, unsigned len) +{ + // because dumbfiles don't have any concept of backward seek or + // rewind, you have to reopen if any loader fails + + dfil = dumbfile_open_memory ((const char *)data, len); + duh = read_duh (dfil); + + if (!duh) + { + dumbfile_close (dfil); + dfil = dumbfile_open_memory ((const char*)data, len); + duh = dumb_read_it_quick (dfil); + } + if (!duh) + { + dumbfile_close (dfil); + dfil = dumbfile_open_memory ((const char*)data, len); + duh = dumb_read_xm_quick (dfil); + } + if (!duh) + { + dumbfile_close (dfil); + dfil = dumbfile_open_memory ((const char*)data, len); + duh = dumb_read_s3m_quick (dfil); + } + if (!duh) + { + dumbfile_close (dfil); + dfil = dumbfile_open_memory ((const char*)data, len); +#if (DUMB_MAJOR_VERSION >= 1) + duh = dumb_read_mod_quick (dfil, 0); +#else + duh = dumb_read_mod_quick (dfil); +#endif + // No way to get the filename, so we can't check for a .mod extension, and + // therefore, trying to load an old 15-instrument SoundTracker module is not + // safe. We'll restrict MOD loading to 31-instrument modules with known + // signatures and let the sound system worry about 15-instrument ones. + // (Assuming it even supports them) + { + DUMB_IT_SIGDATA *sigdata = duh_get_it_sigdata(duh); + if (sigdata) + { + int n_samples = dumb_it_sd_get_n_samples(sigdata); + if (n_samples == 15) + { + unload_duh(duh); + duh = NULL; + } + } + } + } + if (!duh) + { + dumbfile_close (dfil); + dfil = NULL; + return NULL; + } + // handle not used + return data; +} + +static void db_unregistersong (const void *handle) +{ + if (duh) + { + unload_duh (duh); + duh = NULL; + } + + if (dfil) + { + dumbfile_close (dfil); + dfil = NULL; + } +} + +static void db_play (const void *handle, int looping) +{ + dsren = duh_start_sigrenderer (duh, 0, 2, 0); + + if (!dsren) // fail? + { + db_playing = 0; + return; + } + + db_looping = looping; + db_playing = 1; +} + +static void db_stop (void) +{ + duh_end_sigrenderer (dsren); + dsren = NULL; + db_playing = 0; +} + +static void db_pause (void) +{ + db_paused = 1; +} + +static void db_resume (void) +{ + db_paused = 0; +} + +static void db_render (void *dest, unsigned nsamp) +{ + unsigned char *cdest = (unsigned char *)dest; + unsigned nsampwrit = 0; + + if (db_playing && !db_paused) + { +#if ( DUMB_MAJOR_VERSION >= 2 ) + sample_t **sig_samples = NULL; + long sig_samples_size = 0; + + nsampwrit = duh_render_int(dsren, &sig_samples, &sig_samples_size, + 16, 0, db_volume, db_delta, nsamp, dest); + destroy_sample_buffer(sig_samples); +#else + nsampwrit = duh_render(dsren, 16, 0, db_volume, db_delta, nsamp, dest); +#endif + if (nsampwrit != nsamp) + { // end of file + // tracker formats can have looping imbedded in them, in which case + // we'll never reach this (even if db_looping is 0!!) + + cdest += nsampwrit * 4; + + + if (db_looping) + { // but if the tracker doesn't loop, and we want loop anyway, restart + // from beginning + + if (nsampwrit == 0) + { // special case: avoid infinite recursion + db_stop (); + lprintf (LO_WARN, "db_render: problem (0 length tracker file on loop?\n"); + return; + } + + // im not sure if this is the best way to seek, but there isn't + // a sigrenderer_rewind type function + db_stop (); + db_play (NULL, 1); + db_render (cdest, nsamp - nsampwrit); + } + else + { // halt + db_stop (); + memset (cdest, 0, (nsamp - nsampwrit) * 4); + } + } + } + else + memset (dest, 0, nsamp * 4); +} + +const music_player_t db_player = +{ + db_name, + db_init, + db_shutdown, + db_setvolume, + db_pause, + db_resume, + db_registersong, + db_unregistersong, + db_play, + db_stop, + db_render +}; + + + + +#endif // HAVE_DUMB diff --git a/src/MUSIC/dumbplayer.h b/src/MUSIC/dumbplayer.h new file mode 100644 index 0000000..dc71132 --- /dev/null +++ b/src/MUSIC/dumbplayer.h @@ -0,0 +1,46 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * + * Copyright (C) 2011 by + * Nicholai Main + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifndef DUMBPLAYER_H +#define DUMBPLAYER_H + + + + +extern const music_player_t db_player; + + + + + + + + +#endif // DUMBPLAYER_H diff --git a/src/MUSIC/flplayer.c b/src/MUSIC/flplayer.c new file mode 100644 index 0000000..f49aec9 --- /dev/null +++ b/src/MUSIC/flplayer.c @@ -0,0 +1,529 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * + * Copyright (C) 2011 by + * Nicholai Main + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "musicplayer.h" + +#ifndef HAVE_LIBFLUIDSYNTH +#include + +static const char *fl_name (void) +{ + return "fluidsynth midi player (DISABLED)"; +} + + +static int fl_init (int samplerate) +{ + return 0; +} + +const music_player_t fl_player = +{ + fl_name, + fl_init, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +#else // HAVE_LIBFLUIDSYNTH + +#include +#include "i_sound.h" // for snd_soundfont, mus_fluidsynth_gain +#include "i_system.h" // for I_FindFile() +#include "lprintf.h" +#include "midifile.h" +#include +#include + +static fluid_settings_t *f_set; +static fluid_synth_t *f_syn; +static int f_font; +static midi_event_t **events; +static int eventpos; +static midi_file_t *midifile; + +static int f_playing; +static int f_paused; +static int f_looping; +static int f_volume; +static double spmc; +static double f_delta; +static int f_soundrate; + +#define SYSEX_BUFF_SIZE 1024 +static unsigned char sysexbuff[SYSEX_BUFF_SIZE]; +static int sysexbufflen; + +static const char *fl_name (void) +{ + return "fluidsynth midi player"; +} + +#ifdef _MSC_VER +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#include +#endif + +static int fl_init (int samplerate) +{ + const char *filename; + + TESTDLLLOAD ("libfluidsynth.dll", TRUE) + + f_soundrate = samplerate; + // fluidsynth 1.1.4 supports sample rates as low as 8000hz. earlier versions only go down to 22050hz + // since the versions are ABI compatible, detect at runtime, not compile time + if (1) + { + int sratemin; + int major; + int minor; + int micro; + fluid_version (&major, &minor, µ); + lprintf (LO_INFO, "Fluidplayer: Fluidsynth version %i.%i.%i\n", major, minor, micro); + if (major >= 2 || (minor >=1 && micro >= 4)) + sratemin = 8000; + else + sratemin = 22050; + if (f_soundrate < sratemin) + { + lprintf (LO_INFO, "Fluidplayer: samplerates under %i are not supported\n", sratemin); + return 0; + } + } + + + f_set = new_fluid_settings (); + +#if FLUIDSYNTH_VERSION_MAJOR == 1 + #define FSET(a,b,c) if (!fluid_settings_set##a(f_set,b,c))\ + lprintf (LO_INFO, "fl_init: Couldn't set " b "\n") +#else + #define FSET(a,b,c) if (fluid_settings_set##a(f_set,b,c) == FLUID_FAILED)\ + lprintf (LO_INFO, "fl_init: Couldn't set " b "\n") +#endif + + FSET (num, "synth.sample-rate", f_soundrate); + + FSET (int, "synth.chorus.active", mus_fluidsynth_chorus); + FSET (int, "synth.reverb.active", mus_fluidsynth_reverb); + + if (mus_fluidsynth_chorus) + { + FSET (num, "synth.chorus.depth", (double) 5); + FSET (num, "synth.chorus.level", (double) 0.35); + } + + if (mus_fluidsynth_reverb) + { + FSET (num, "synth.reverb.damp", (double) 0.4); + FSET (num, "synth.reverb.level", (double) 0.15); + FSET (num, "synth.reverb.width", (double) 4); + FSET (num, "synth.reverb.room-size", (double) 0.6); + } + + // gain control + FSET (num, "synth.gain", mus_fluidsynth_gain / 100.0); // 0.0 - 0.2 - 10.0 + // behavior wrt bank select messages + FSET (str, "synth.midi-bank-select", "gs"); // fluidsynth default + // general midi spec says 24 voices, but modern midi songs use more + FSET (int, "synth.polyphony", 256); // fluidsynth default + + // we're not using the builtin shell or builtin midiplayer, + // and our own access to the synth is protected by mutex in i_sound.c + FSET (int, "synth.threadsafe-api", 0); +#if FLUIDSYNTH_VERSION_MAJOR == 1 + FSET (int, "synth.parallel-render", 0); +#endif + + // prints debugging information to STDOUT + //FSET (int, "synth.verbose", 1); + + #undef FSET + + f_syn = new_fluid_synth (f_set); + if (!f_syn) + { + lprintf (LO_WARN, "fl_init: error creating fluidsynth object\n"); + delete_fluid_settings (f_set); + return 0; + } + + filename = I_FindFile2(snd_soundfont, ".sf2"); + f_font = fluid_synth_sfload (f_syn, filename, 1); + + if (f_font == FLUID_FAILED) + { + lprintf (LO_WARN, "fl_init: error loading soundfont %s\n", snd_soundfont); + delete_fluid_synth (f_syn); + delete_fluid_settings (f_set); + return 0; + } + + return 1; +} + +static void fl_shutdown (void) +{ + if (f_syn) + { + fluid_synth_sfunload (f_syn, f_font, 1); + delete_fluid_synth (f_syn); + f_syn = NULL; + f_font = 0; + } + + if (f_set) + { + delete_fluid_settings (f_set); + f_set = NULL; + } +} + + + + + +static const void *fl_registersong (const void *data, unsigned len) +{ + midimem_t mf; + + mf.len = len; + mf.pos = 0; + mf.data = (const byte*)data; + + midifile = MIDI_LoadFile (&mf); + + if (!midifile) + { + lprintf (LO_WARN, "fl_registersong: Failed to load MIDI.\n"); + return NULL; + } + + events = MIDI_GenerateFlatList (midifile); + if (!events) + { + MIDI_FreeFile (midifile); + return NULL; + } + eventpos = 0; + + // implicit 120BPM (this is correct to spec) + //spmc = compute_spmc (MIDI_GetFileTimeDivision (midifile), 500000, f_soundrate); + spmc = MIDI_spmc (midifile, NULL, f_soundrate); + + // handle not used + return data; +} + +static void fl_unregistersong (const void *handle) +{ + if (events) + { + MIDI_DestroyFlatList (events); + events = NULL; + } + if (midifile) + { + MIDI_FreeFile (midifile); + midifile = NULL; + } +} + +static void fl_pause (void) +{ + //int i; + f_paused = 1; + // instead of cutting notes, pause the synth so they can resume seamlessly + //for (i = 0; i < 16; i++) + // fluid_synth_cc (f_syn, i, 123, 0); // ALL NOTES OFF +} +static void fl_resume (void) +{ + f_paused = 0; +} +static void fl_play (const void *handle, int looping) +{ + eventpos = 0; + f_looping = looping; + f_playing = 1; + //f_paused = 0; + f_delta = 0.0; + fluid_synth_program_reset (f_syn); + fluid_synth_system_reset (f_syn); +} + +static void fl_stop (void) +{ + int i; + f_playing = 0; + + for (i = 0; i < 16; i++) + { + fluid_synth_cc (f_syn, i, 123, 0); // ALL NOTES OFF + fluid_synth_cc (f_syn, i, 121, 0); // RESET ALL CONTROLLERS + } +} + +static void fl_setvolume (int v) +{ + f_volume = v; +} + + +static void fl_writesamples_ex (short *dest, int nsamp) +{ // does volume conversion and then writes samples + int i; + float multiplier = 16384.0f / 15.0f * f_volume; + + static float *fbuff = NULL; + static int fbuff_siz = 0; + + if (nsamp * 2 > fbuff_siz) + { + float *newfbuff = (float*)realloc (fbuff, nsamp * 2 * sizeof (float)); + if (!newfbuff) return; + fbuff = newfbuff; + fbuff_siz = nsamp * 2; + } + + fluid_synth_write_float (f_syn, nsamp, fbuff, 0, 2, fbuff, 1, 2); + + for (i = 0; i < nsamp * 2; i++) + { + // data is NOT already clipped + float f = fbuff[i]; + if (f > 1.0f) + f = 1.0f; + if (f < -1.0f) + f = -1.0f; + dest[i] = (short) (f * multiplier); + } +} + +static void writesysex (unsigned char *data, int len) +{ + // sysex code is untested + // it's possible to use an auto-resizing buffer here, but a malformed + // midi file could make it grow arbitrarily large (since it must grow + // until it hits an 0xf7 terminator) + int didrespond = 0; + + if (len + sysexbufflen > SYSEX_BUFF_SIZE) + { + lprintf (LO_WARN, "fluidplayer: ignoring large or malformed sysex message\n"); + sysexbufflen = 0; + return; + } + memcpy (sysexbuff + sysexbufflen, data, len); + sysexbufflen += len; + if (sysexbuff[sysexbufflen - 1] == 0xf7) // terminator + { // pass len-1 because fluidsynth does NOT want the final F7 + fluid_synth_sysex (f_syn, (const char *)sysexbuff, sysexbufflen - 1, NULL, NULL, &didrespond, 0); + sysexbufflen = 0; + } + if (!didrespond) + { + lprintf (LO_WARN, "fluidplayer: SYSEX message received but not understood\n"); + } +} + +static void fl_render (void *vdest, unsigned length) +{ + short *dest = (short*)vdest; + + unsigned sampleswritten = 0; + unsigned samples; + + midi_event_t *currevent; + + if (!f_playing || f_paused) + { + // save CPU time and allow for seamless resume after pause + memset (vdest, 0, length * 4); + //fl_writesamples_ex (vdest, length); + return; + } + + + while (1) + { + double eventdelta; + currevent = events[eventpos]; + + // how many samples away event is + eventdelta = currevent->delta_time * spmc; + + + // how many we will render (rounding down); include delta offset + samples = (unsigned) (eventdelta + f_delta); + + + if (samples + sampleswritten > length) + { // overshoot; render some samples without processing an event + break; + } + + + if (samples) + { + fl_writesamples_ex (dest, samples); + sampleswritten += samples; + f_delta -= samples; + dest += samples * 2; + } + + // process event + switch (currevent->event_type) + { + case MIDI_EVENT_NOTE_OFF: + fluid_synth_noteoff (f_syn, currevent->data.channel.channel, currevent->data.channel.param1); + break; + case MIDI_EVENT_NOTE_ON: + fluid_synth_noteon (f_syn, currevent->data.channel.channel, currevent->data.channel.param1, currevent->data.channel.param2); + break; + case MIDI_EVENT_AFTERTOUCH: + // not suipported? + break; + case MIDI_EVENT_CONTROLLER: + fluid_synth_cc (f_syn, currevent->data.channel.channel, currevent->data.channel.param1, currevent->data.channel.param2); + break; + case MIDI_EVENT_PROGRAM_CHANGE: + fluid_synth_program_change (f_syn, currevent->data.channel.channel, currevent->data.channel.param1); + break; + case MIDI_EVENT_CHAN_AFTERTOUCH: + fluid_synth_channel_pressure (f_syn, currevent->data.channel.channel, currevent->data.channel.param1); + break; + case MIDI_EVENT_PITCH_BEND: + fluid_synth_pitch_bend (f_syn, currevent->data.channel.channel, currevent->data.channel.param1 | currevent->data.channel.param2 << 7); + break; + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + writesysex (currevent->data.sysex.data, currevent->data.sysex.length); + break; + case MIDI_EVENT_META: + if (currevent->data.meta.type == MIDI_META_SET_TEMPO) + spmc = MIDI_spmc (midifile, currevent, f_soundrate); + else if (currevent->data.meta.type == MIDI_META_END_OF_TRACK) + { + if (f_looping) + { + int i; + eventpos = 0; + f_delta += eventdelta; + // fix buggy songs that forget to terminate notes held over loop point + // sdl_mixer does this as well + for (i = 0; i < 16; i++) + { + fluid_synth_cc (f_syn, i, 123, 0); // ALL NOTES OFF + fluid_synth_cc (f_syn, i, 121, 0); // RESET ALL CONTROLLERS + } + continue; + } + // stop, write leadout + fl_stop (); + samples = length - sampleswritten; + if (samples) + { + fl_writesamples_ex (dest, samples); + sampleswritten += samples; + // timecodes no longer relevant + dest += samples * 2; + + } + return; + } + break; // not interested in most metas + default: //uhh + break; + + } + // event processed so advance midiclock + f_delta += eventdelta; + eventpos++; + + } + + + + + if (samples + sampleswritten > length) + { // broke due to next event being past the end of current render buffer + // finish buffer, return + samples = length - sampleswritten; + if (samples) + { + fl_writesamples_ex (dest, samples); + sampleswritten += samples; + f_delta -= samples; // save offset + dest += samples * 2; + } + } + else + { // huh? + return; + } + + +} + + +const music_player_t fl_player = +{ + fl_name, + fl_init, + fl_shutdown, + fl_setvolume, + fl_pause, + fl_resume, + fl_registersong, + fl_unregistersong, + fl_play, + fl_stop, + fl_render +}; + + +#endif // HAVE_LIBFLUIDSYNTH + diff --git a/src/MUSIC/flplayer.h b/src/MUSIC/flplayer.h new file mode 100644 index 0000000..2149b2b --- /dev/null +++ b/src/MUSIC/flplayer.h @@ -0,0 +1,47 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * + * Copyright (C) 2011 by + * Nicholai Main + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifndef FLPLAYER_H +#define FLPLAYER_H + + + + +extern const music_player_t fl_player; + + + + + + + + + +#endif // FLPLAYER_H diff --git a/src/MUSIC/madplayer.c b/src/MUSIC/madplayer.c new file mode 100644 index 0000000..f3a3e19 --- /dev/null +++ b/src/MUSIC/madplayer.c @@ -0,0 +1,335 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * + * Copyright (C) 2011 by + * Nicholai Main + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "musicplayer.h" + +#ifndef HAVE_LIBMAD +#include + +static const char *mp_name (void) +{ + return "mad mp3 player (DISABLED)"; +} + + +static int mp_init (int samplerate) +{ + return 0; +} + +const music_player_t mp_player = +{ + mp_name, + mp_init, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +#else // HAVE_LIBMAD + + +#include +#include +#include "lprintf.h" + +#include + +#include "i_sound.h" + +static struct mad_stream Stream; +static struct mad_frame Frame; +static struct mad_synth Synth; +static struct mad_header Header; + + +static int mp_looping = 0; +static int mp_volume = 0; // 0-15 +static int mp_samplerate_target = 0; +static int mp_paused = 0; +static int mp_playing = 0; + +static const void *mp_data; +static int mp_len; + + +static int mp_leftoversamps = 0; // number of extra samples + // left over in mad decoder +static int mp_leftoversamppos = 0; + + +static const char *mp_name (void) +{ + return "mad mp3 player"; +} + + +static int mp_init (int samplerate) +{ + mad_stream_init (&Stream); + mad_frame_init (&Frame); + mad_synth_init (&Synth); + mad_header_init (&Header); + mp_samplerate_target = samplerate; + return 1; +} + +static void mp_shutdown (void) +{ + + mad_synth_finish (&Synth); + mad_frame_finish (&Frame); + mad_stream_finish (&Stream); + mad_header_finish (&Header); +} + +static const void *mp_registersong (const void *data, unsigned len) +{ + int i; + int maxtry; + int success = 0; + + // the MP3 standard doesn't include any global file header. the only way to tell filetype + // is to start decoding stuff. you can't be too strict however because MP3 is resilient to + // crap in the stream. + + // this routine is a bit slower than it could be, but apparently there are lots of files out + // there with some dodgy stuff at the beginning. + + // if the stream begins with an ID3v2 magic, search hard and long for our first valid header + if (memcmp (data, "ID3", 3) == 0) + maxtry = 100; + // otherwise, search for not so long + else + maxtry = 20; + + mad_stream_buffer (&Stream, (const unsigned char*)data, len); + + for (i = 0; i < maxtry; i++) + { + if (mad_header_decode (&Header, &Stream) != 0) + { + if (!MAD_RECOVERABLE (Stream.error)) + { + lprintf (LO_WARN, "mad_registersong failed: %s\n", mad_stream_errorstr (&Stream)); + return NULL; + } + } + else + { + success++; + } + } + + // 80% to pass + if (success < maxtry * 8 / 10) + { + lprintf (LO_WARN, "mad_registersong failed\n"); + return NULL; + } + + lprintf (LO_INFO, "mad_registersong succeed. bitrate %lu samplerate %d\n", Header.bitrate, Header.samplerate); + + mp_data = data; + mp_len = len; + // handle not used + return data; +} + +static void mp_setvolume (int v) +{ + mp_volume = v; +} + +static void mp_pause (void) +{ + mp_paused = 1; +} + +static void mp_resume (void) +{ + mp_paused = 0; +} + +static void mp_unregistersong (const void *handle) +{ // nothing to do + mp_data = NULL; + mp_playing = 0; +} + +static void mp_play (const void *handle, int looping) +{ + mad_stream_buffer (&Stream, (const unsigned char *)mp_data, mp_len); + + mp_playing = 1; + mp_looping = looping; + mp_leftoversamps = 0; + mp_leftoversamppos = 0; +} + +static void mp_stop (void) +{ + mp_playing = 0; +} + +// convert from mad's internal fixed point representation +static inline short mp_fixtoshort (mad_fixed_t f) +{ + // clip + if (f < -MAD_F_ONE) + f = -MAD_F_ONE; + if (f > MAD_F_ONE) + f = MAD_F_ONE; + // apply volume before conversion to 16bit + f /= 15; + f *= mp_volume; + f >>= (MAD_F_FRACBITS - 15); + + return (short) f; +} + +static void mp_render_ex (void *dest, unsigned nsamp) +{ + short *sout = (short *) dest; + + int localerrors = 0; + + if (!mp_playing || mp_paused) + { + memset (dest, 0, nsamp * 4); + return; + } + + while (1) + { + // write any leftover data from last MP3 frame + while (mp_leftoversamps > 0 && nsamp > 0) + { + short s = mp_fixtoshort (Synth.pcm.samples[0][mp_leftoversamppos]); + *sout++ = s; + if (Synth.pcm.channels == 2) + s = mp_fixtoshort (Synth.pcm.samples[1][mp_leftoversamppos]); + // if mono, just duplicate the first channel again + *sout++ = s; + + mp_leftoversamps -= 1; + mp_leftoversamppos += 1; + nsamp -= 1; + } + if (nsamp == 0) + return; // done + + // decode next valid MP3 frame + while (mad_frame_decode (&Frame, &Stream) != 0) + { + if (MAD_RECOVERABLE (Stream.error)) + { // unspecified problem with one frame. + // try the next frame, but bail if we get a bunch of crap in a row; + // likely indicates a larger problem (and if we don't bail, we could + // spend arbitrarily long amounts of time looking for the next good + // packet) + localerrors++; + if (localerrors == 10) + { + lprintf (LO_WARN, "mad_frame_decode: Lots of errors. Most recent %s\n", mad_stream_errorstr (&Stream)); + mp_playing = 0; + memset (sout, 0, nsamp * 4); + return; + } + } + else if (Stream.error == MAD_ERROR_BUFLEN) + { // EOF + // FIXME: in order to not drop the last frame, there must be at least MAD_BUFFER_GUARD + // of extra bytes (with value 0) at the end of the file. current implementation + // drops last frame + if (mp_looping) + { // rewind, then go again + mad_stream_buffer (&Stream, (const unsigned char *)mp_data, mp_len); + continue; + } + else + { // stop + mp_playing = 0; + memset (sout, 0, nsamp * 4); + return; + } + } + else + { // oh well. + lprintf (LO_WARN, "mad_frame_decode: Unrecoverable error %s\n", mad_stream_errorstr (&Stream)); + mp_playing = 0; + memset (sout, 0, nsamp * 4); + return; + } + } + + // got a good frame, so synth it and dispatch it. + mad_synth_frame (&Synth, &Frame); + mp_leftoversamps = Synth.pcm.length; + mp_leftoversamppos = 0; + + } + // NOT REACHED +} + +static void mp_render (void *dest, unsigned nsamp) +{ + I_ResampleStream (dest, nsamp, mp_render_ex, Header.samplerate, mp_samplerate_target); +} + + +const music_player_t mp_player = +{ + mp_name, + mp_init, + mp_shutdown, + mp_setvolume, + mp_pause, + mp_resume, + mp_registersong, + mp_unregistersong, + mp_play, + mp_stop, + mp_render +}; + +#endif // HAVE_LIBMAD diff --git a/src/MUSIC/madplayer.h b/src/MUSIC/madplayer.h new file mode 100644 index 0000000..989c398 --- /dev/null +++ b/src/MUSIC/madplayer.h @@ -0,0 +1,46 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * + * Copyright (C) 2011 by + * Nicholai Main + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifndef MADPLAYER_H +#define MADPLAYER_H + + + +extern const music_player_t mp_player; + + + + + + + + + +#endif // MADPLAYER_H diff --git a/src/MUSIC/midifile.c b/src/MUSIC/midifile.c new file mode 100644 index 0000000..17480ed --- /dev/null +++ b/src/MUSIC/midifile.c @@ -0,0 +1,1272 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// Reading of MIDI files. +// +//----------------------------------------------------------------------------- + +#include +#include +#include +#include + +#ifndef TEST +#include "doomdef.h" +#include "doomtype.h" +#else +typedef enum {false, true} dboolean; +typedef unsigned char byte; +#define PACKEDATTR __attribute__((packed)) +#endif +#include "lprintf.h" +#include "midifile.h" + +#define HEADER_CHUNK_ID "MThd" +#define TRACK_CHUNK_ID "MTrk" +#define MAX_BUFFER_SIZE 0x10000 + + + + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef ntohl +#ifdef WORDS_BIGENDIAN +#define ntohl +#define ntohs +#else // WORDS_BIGENDIAN + +#define ntohl(x) \ + (/*(long int)*/((((unsigned long int)(x) & 0x000000ffU) << 24) | \ + (((unsigned long int)(x) & 0x0000ff00U) << 8) | \ + (((unsigned long int)(x) & 0x00ff0000U) >> 8) | \ + (((unsigned long int)(x) & 0xff000000U) >> 24))) + +#define ntohs(x) \ + (/*(short int)*/((((unsigned short int)(x) & 0x00ff) << 8) | \ + (((unsigned short int)(x) & 0xff00) >> 8))) +#endif // WORDS_BIGENDIAN +#endif // ntohl + + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif + +typedef struct +{ + byte chunk_id[4]; + unsigned int chunk_size; +} PACKEDATTR chunk_header_t; + +typedef struct +{ + chunk_header_t chunk_header; + unsigned short format_type; + unsigned short num_tracks; + unsigned short time_division; +} PACKEDATTR midi_header_t; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif + +typedef struct +{ + // Length in bytes: + + unsigned int data_len; + + // Events in this track: + + midi_event_t *events; + unsigned int num_events; + unsigned int num_event_mem; // NSM track size of structure +} midi_track_t; + +struct midi_track_iter_s +{ + midi_track_t *track; + unsigned int position; +}; + +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; +}; + + + +// Check the header of a chunk: + +static dboolean CheckChunkHeader(chunk_header_t *chunk, + const char *expected_id) +{ + dboolean result; + + result = (memcmp((char *) chunk->chunk_id, expected_id, 4) == 0); + + if (!result) + { + lprintf (LO_WARN, "CheckChunkHeader: 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 dboolean ReadByte(byte *result, midimem_t *mf) +{ + if (mf->pos >= mf->len) + { + lprintf (LO_WARN, "ReadByte: Unexpected end of file\n"); + return false; + } + + *result = mf->data[mf->pos++]; + return true; +} + +static dboolean ReadMultipleBytes (void *dest, size_t len, midimem_t *mf) +{ + byte *cdest = (byte *) dest; + unsigned i; + for (i = 0; i < len; i++) + { + if (!ReadByte (cdest + i, mf)) + { + lprintf (LO_WARN, "ReadMultipleBytes: Unexpected end of file\n"); + return false; + } + } + return true; +} + +// Read a variable-length value. + +static dboolean ReadVariableLength(unsigned int *result, midimem_t *mf) +{ + int i; + byte b; + + *result = 0; + + for (i=0; i<4; ++i) + { + if (!ReadByte(&b, mf)) + { + lprintf (LO_WARN, "ReadVariableLength: 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; + } + } + + lprintf (LO_WARN, "ReadVariableLength: Variable-length value too " + "long: maximum of four bytes\n"); + return false; +} + +// Read a byte sequence into the data buffer. + +static void *ReadByteSequence(unsigned int num_bytes, midimem_t *mf) +{ + unsigned int i; + byte *result; + + // events can be length 0. malloc(0) is not portable (can return NULL) + if (!num_bytes) + return malloc (4); + + // Allocate a buffer: + + result = (byte*)malloc(num_bytes); + + if (result == NULL) + { + lprintf (LO_WARN, "ReadByteSequence: Failed to allocate buffer %u bytes\n", num_bytes); + return NULL; + } + + // Read the data: + + for (i=0; ievent_type = (midi_event_type_t)(event_type & 0xf0); + event->data.channel.channel = event_type & 0x0f; + + // Read parameters: + + if (!ReadByte(&b, mf)) + { + lprintf (LO_WARN, "ReadChannelEvent: Error while reading channel " + "event parameters\n"); + return false; + } + + event->data.channel.param1 = b; + + // Second parameter: + + if (two_param) + { + if (!ReadByte(&b, mf)) + { + lprintf (LO_WARN, "ReadChannelEvent: Error while reading channel " + "event parameters\n"); + return false; + } + + event->data.channel.param2 = b; + } + + return true; +} + +// Read sysex event: + +static dboolean ReadSysExEvent(midi_event_t *event, int event_type, + midimem_t *mf) +{ + event->event_type = (midi_event_type_t)event_type; + + if (!ReadVariableLength(&event->data.sysex.length, mf)) + { + lprintf (LO_WARN, "ReadSysExEvent: Failed to read length of " + "SysEx block\n"); + return false; + } + + // Read the byte sequence: + + event->data.sysex.data = (byte*)ReadByteSequence(event->data.sysex.length, mf); + + if (event->data.sysex.data == NULL) + { + lprintf (LO_WARN, "ReadSysExEvent: Failed while reading SysEx event\n"); + return false; + } + + return true; +} + +// Read meta event: + +static dboolean ReadMetaEvent(midi_event_t *event, midimem_t *mf) +{ + byte b; + + event->event_type = MIDI_EVENT_META; + + // Read meta event type: + + if (!ReadByte(&b, mf)) + { + lprintf (LO_WARN, "ReadMetaEvent: Failed to read meta event type\n"); + return false; + } + + event->data.meta.type = b; + + // Read length of meta event data: + + if (!ReadVariableLength(&event->data.meta.length, mf)) + { + lprintf (LO_WARN, "ReadMetaEvent: Failed to read length of " + "MetaEvent block\n"); + return false; + } + + // Read the byte sequence: + + event->data.meta.data = (byte*)ReadByteSequence(event->data.meta.length, mf); + + if (event->data.meta.data == NULL) + { + lprintf (LO_WARN, "ReadMetaEvent: Failed while reading MetaEvent\n"); + return false; + } + + return true; +} + +static dboolean ReadEvent(midi_event_t *event, unsigned int *last_event_type, + midimem_t *mf) +{ + byte event_type; + + if (!ReadVariableLength(&event->delta_time, mf)) + { + lprintf (LO_WARN, "ReadEvent: Failed to read event timestamp\n"); + return false; + } + + if (!ReadByte(&event_type, mf)) + { + lprintf (LO_WARN, "ReadEvent: 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; + mf->pos--; + } + 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 ReadChannelEvent(event, event_type, true, mf); + + // Single parameter channel events: + + case MIDI_EVENT_PROGRAM_CHANGE: + case MIDI_EVENT_CHAN_AFTERTOUCH: + return ReadChannelEvent(event, event_type, false, mf); + + default: + break; + } + + // Specific value? + + switch (event_type) + { + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + return ReadSysExEvent(event, event_type, mf); + + case MIDI_EVENT_META: + return ReadMetaEvent(event, mf); + + default: + break; + } + + lprintf (LO_WARN, "ReadEvent: Unknown MIDI event type: 0x%x\n", event_type); + return false; +} + +// Free an event: + +static void FreeEvent(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: + // Nothing to do. + break; + } +} + +// Read and check the track chunk header + +static dboolean ReadTrackHeader(midi_track_t *track, midimem_t *mf) +{ + size_t records_read; + chunk_header_t chunk_header; + + records_read = ReadMultipleBytes(&chunk_header, sizeof(chunk_header_t), mf); + + if (records_read < 1) + { + return false; + } + + if (!CheckChunkHeader(&chunk_header, TRACK_CHUNK_ID)) + { + return false; + } + + track->data_len = ntohl(chunk_header.chunk_size); + + return true; +} + +static dboolean ReadTrack(midi_track_t *track, midimem_t *mf) +{ + midi_event_t *new_events = NULL; + midi_event_t *event; + unsigned int last_event_type; + + track->num_events = 0; + track->events = NULL; + track->num_event_mem = 0; // NSM + + // Read the header: + + if (!ReadTrackHeader(track, mf)) + { + return false; + } + + // Then the events: + + last_event_type = 0; + + for (;;) + { + // Resize the track slightly larger to hold another event: + /* + new_events = realloc(track->events, + sizeof(midi_event_t) * (track->num_events + 1)); + */ + if (track->num_events == track->num_event_mem) + { // depending on the state of the heap and the malloc implementation, realloc() + // one more event at a time can be VERY slow. 10sec+ in MSVC + track->num_event_mem += 100; + new_events = (midi_event_t*)realloc (track->events, sizeof (midi_event_t) * track->num_event_mem); + } + + if (new_events == NULL) + { + return false; + } + + track->events = new_events; + + // Read the next event: + + event = &track->events[track->num_events]; + if (!ReadEvent(event, &last_event_type, mf)) + { + 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 FreeTrack(midi_track_t *track) +{ + unsigned i; + + for (i=0; inum_events; ++i) + { + FreeEvent(&track->events[i]); + } + + free(track->events); +} + +static dboolean ReadAllTracks(midi_file_t *file, midimem_t *mf) +{ + unsigned int i; + + // Allocate list of tracks and read each track: + + file->tracks = (midi_track_t*)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; inum_tracks; ++i) + { + if (!ReadTrack(&file->tracks[i], mf)) + { + return false; + } + } + + return true; +} + +// Read and check the header chunk. + +static dboolean ReadFileHeader(midi_file_t *file, midimem_t *mf) +{ + size_t records_read; + unsigned int format_type; + + records_read = ReadMultipleBytes (&file->header, sizeof(midi_header_t), mf); + + if (records_read < 1) + { + return false; + } + + if (!CheckChunkHeader(&file->header.chunk_header, HEADER_CHUNK_ID) + || ntohl(file->header.chunk_header.chunk_size) != 6) + { + lprintf (LO_WARN, "ReadFileHeader: Invalid MIDI chunk header! " + "chunk_size=%ld\n", + ntohl(file->header.chunk_header.chunk_size)); + return false; + } + + format_type = ntohs(file->header.format_type); + file->num_tracks = ntohs(file->header.num_tracks); + + if ((format_type != 0 && format_type != 1) + || file->num_tracks < 1) + { + lprintf (LO_WARN, "ReadFileHeader: Only type 0/1 " + "MIDI files supported!\n"); + return false; + } + // NSM + file->header.time_division = ntohs (file->header.time_division); + + + return true; +} + +void MIDI_FreeFile(midi_file_t *file) +{ + unsigned i; + + if (file->tracks != NULL) + { + for (i=0; inum_tracks; ++i) + { + FreeTrack(&file->tracks[i]); + } + + free(file->tracks); + } + + free(file); +} + +midi_file_t *MIDI_LoadFile (midimem_t *mf) +{ + midi_file_t *file; + + file = (midi_file_t*)malloc(sizeof(midi_file_t)); + + if (file == NULL) + { + return NULL; + } + + file->tracks = NULL; + file->num_tracks = 0; + file->buffer = NULL; + file->buffer_size = 0; + + // Read MIDI file header + + if (!ReadFileHeader(file, mf)) + { + MIDI_FreeFile(file); + return NULL; + } + + // Read all tracks: + + if (!ReadAllTracks(file, mf)) + { + MIDI_FreeFile(file); + return NULL; + } + + return file; +} + +// Get the number of tracks in a MIDI file. + +unsigned int MIDI_NumTracks(const midi_file_t *file) +{ + return file->num_tracks; +} + +// Start iterating over the events in a track. + +midi_track_iter_t *MIDI_IterateTrack(const midi_file_t *file, unsigned int track) +{ + midi_track_iter_t *iter; + + assert(track < file->num_tracks); + + iter = (midi_track_iter_t*)malloc(sizeof(*iter)); + iter->track = &file->tracks[track]; + iter->position = 0; + + return iter; +} + +void MIDI_FreeIterator(midi_track_iter_t *iter) +{ + free(iter); +} + +// Get the time until the next MIDI event in a track. + +unsigned int MIDI_GetDeltaTime(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_GetNextEvent(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_GetFileTimeDivision(const midi_file_t *file) +{ + return file->header.time_division; +} + +void MIDI_RestartIterator(midi_track_iter_t *iter) +{ + iter->position = 0; +} + + + +static void MIDI_PrintFlatListDBG (const midi_event_t **evs) +{ + const midi_event_t *event; + + while (1) + { + event = *evs++; + + if (event->delta_time > 0) + printf("Delay: %i ticks\n", event->delta_time); + + + switch (event->event_type) + { + case MIDI_EVENT_NOTE_OFF: + printf ("MIDI_EVENT_NOTE_OFF\n");break; + case MIDI_EVENT_NOTE_ON: + printf ("MIDI_EVENT_NOTE_ON\n");break; + case MIDI_EVENT_AFTERTOUCH: + printf ("MIDI_EVENT_AFTERTOUCH\n");break; + case MIDI_EVENT_CONTROLLER: + printf ("MIDI_EVENT_CONTROLLER\n");break; + case MIDI_EVENT_PROGRAM_CHANGE: + printf ("MIDI_EVENT_PROGRAM_CHANGE\n");break; + case MIDI_EVENT_CHAN_AFTERTOUCH: + printf ("MIDI_EVENT_CHAN_AFTERTOUCH\n");break; + case MIDI_EVENT_PITCH_BEND: + printf ("MIDI_EVENT_PITCH_BEND\n");break; + case MIDI_EVENT_SYSEX: + printf ("MIDI_EVENT_SYSEX\n");break; + case MIDI_EVENT_SYSEX_SPLIT: + printf ("MIDI_EVENT_SYSEX_SPLIT\n");break; + case MIDI_EVENT_META: + printf ("MIDI_EVENT_META\n");break; + + default: + printf ("(unknown)\n");break; + } + 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: %i\n", event->data.channel.channel); + printf("\tParameter 1: %i\n", event->data.channel.param1); + printf("\tParameter 2: %i\n", event->data.channel.param2); + break; + + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + printf("\tLength: %i\n", event->data.sysex.length); + break; + + case MIDI_EVENT_META: + printf("\tMeta type: %i\n", event->data.meta.type); + printf("\tLength: %i\n", event->data.meta.length); + break; + } + if (event->event_type == MIDI_EVENT_META && + event->data.meta.type == MIDI_META_END_OF_TRACK) + { + printf ("gotta go!\n"); + return; + } + } +} + + + + + + +// NSM: an alternate iterator tool. + +midi_event_t **MIDI_GenerateFlatList (midi_file_t *file) +{ + int totalevents = 0; + unsigned i, delta; + int nextrk; + + int totaldelta = 0; + + int *trackpos = (int*)calloc (file->num_tracks, sizeof (int)); + int *tracktime = (int*)calloc (file->num_tracks, sizeof (int)); + int trackactive = file->num_tracks; + + midi_event_t **ret; + midi_event_t **epos; + + for (i = 0; i < file->num_tracks; i++) + totalevents += file->tracks[i].num_events; + + ret = (midi_event_t**)malloc (totalevents * sizeof (midi_event_t **)); + + epos = ret; + + while (trackactive) + { + delta = 0x10000000; + nextrk = -1; + for (i = 0; i < file->num_tracks; i++) + { + if (trackpos[i] != -1 && file->tracks[i].events[trackpos[i]].delta_time - tracktime[i] < delta) + { + delta = file->tracks[i].events[trackpos[i]].delta_time - tracktime[i]; + nextrk = i; + } + } + if (nextrk == -1) // unexpected EOF (not every track got end track) + break; + + *epos = file->tracks[nextrk].events + trackpos[nextrk]; + + for (i = 0; i < file->num_tracks; i++) + { + if (i == (unsigned) nextrk) + { + tracktime[i] = 0; + trackpos[i]++; + } + else + tracktime[i] += delta; + } + // yes, this clobbers the original timecodes + epos[0]->delta_time = delta; + totaldelta += delta; + + if (epos[0]->event_type == MIDI_EVENT_META + && epos[0]->data.meta.type == MIDI_META_END_OF_TRACK) + { // change end of track into no op + trackactive--; + trackpos[nextrk] = -1; + epos[0]->data.meta.type = MIDI_META_TEXT; + } + else if ((unsigned) trackpos[nextrk] == file->tracks[nextrk].num_events) + { + lprintf (LO_WARN, "MIDI_GenerateFlatList: Unexpected end of track\n"); + free (trackpos); + free (tracktime); + free (ret); + return NULL; + } + epos++; + } + + if (trackactive) + { // unexpected EOF + lprintf (LO_WARN, "MIDI_GenerateFlatList: Unexpected end of midi file\n"); + free (trackpos); + free (tracktime); + free (ret); + return NULL; + } + + // last end of track event is preserved though + epos[-1]->data.meta.type = MIDI_META_END_OF_TRACK; + + free (trackpos); + free (tracktime); + + if (totaldelta < 100) + { + lprintf (LO_WARN, "MIDI_GeneratFlatList: very short file %i\n", totaldelta); + free (ret); + return NULL; + } + + + //MIDI_PrintFlatListDBG (ret); + + return ret; +} + + + +void MIDI_DestroyFlatList (midi_event_t **evs) +{ + free (evs); +} + + + +// NSM: timing assist functions +// they compute samples per midi clock, where midi clock is the units +// that the time deltas are in, and samples is an arbitrary unit of which +// "sndrate" of them occur per second + + +static double compute_spmc_normal (unsigned mpq, unsigned tempo, unsigned sndrate) +{ // returns samples per midi clock + + // inputs: mpq (midi clocks per quarternote, from header) + // tempo (from tempo event, in microseconds per quarternote) + // sndrate (sound sample rate in hz) + + // samples quarternote microsec samples second + // ------- = ----------- * ----------- * ------- * -------- + // midiclk midiclk quarternote second microsec + + // return = (1 / mpq) * tempo * sndrate * (1 / 1000000) + + return (double) tempo / 1000000 * sndrate / mpq; + +} + +static double compute_spmc_smpte (unsigned smpte_fps, unsigned mpf, unsigned sndrate) +{ // returns samples per midi clock + + // inputs: smpte_fps (24, 25, 29, 30) + // mpf (midi clocks per frame, 0-255) + // sndrate (sound sample rate in hz) + + // tempo is ignored here + + // samples frame seconds samples + // ------- = --------- * --------- * ------- + // midiclk midiclk frame second + + // return = (1 / mpf) * (1 / fps) * sndrate + + + double fps; // actual frames per second + switch (smpte_fps) + { + case 24: + case 25: + case 30: + fps = smpte_fps; + break; + case 29: + // i hate NTSC, i really do + fps = smpte_fps * 1000.0 / 1001.0; + break; + default: + lprintf (LO_WARN, "MIDI_spmc: Unexpected SMPTE timestamp %i\n", smpte_fps); + // assume + fps = 30.0; + break; + } + + return (double) sndrate / fps / mpf; + +} + +// if event is NULL, compute with default starting tempo (120BPM) + +double MIDI_spmc (const midi_file_t *file, const midi_event_t *ev, unsigned sndrate) +{ + int smpte_fps; + unsigned tempo; + unsigned headerval = MIDI_GetFileTimeDivision (file); + + if (headerval & 0x8000) // SMPTE + { // i don't really have any files to test this on... + smpte_fps = -(short) headerval >> 8; + return compute_spmc_smpte (smpte_fps, headerval & 0xff, sndrate); + } + + // normal timing + tempo = 500000; // default 120BPM + if (ev) + { + if (ev->event_type == MIDI_EVENT_META) + { + if (ev->data.meta.length == 3) + { + tempo = (unsigned) ev->data.meta.data[0] << 16 | + (unsigned) ev->data.meta.data[1] << 8 | + (unsigned) ev->data.meta.data[2]; + } + else + lprintf (LO_WARN, "MIDI_spmc: wrong length tempo meta message in midi file\n"); + } + else + lprintf (LO_WARN, "MIDI_spmc: passed non-meta event\n"); + } + + return compute_spmc_normal (headerval, tempo, sndrate); +} + +/* +The timing system used by the OPL driver is very interesting. But there are too many edge cases +in multitrack (type 1) midi tempo changes that it simply can't handle without a major rework. +The alternative is that we recook the file into a single track file with no tempo changes at +load time. +*/ + +midi_file_t *MIDI_LoadFileSpecial (midimem_t *mf) +{ + midi_event_t **flatlist; + midi_file_t *base = MIDI_LoadFile (mf); + midi_file_t *ret; + + double opi; + + int epos = 0; + + if (!base) + return NULL; + + flatlist = MIDI_GenerateFlatList (base); + if (!flatlist) + { + MIDI_FreeFile (base); + return NULL; + } + + ret = (midi_file_t*)malloc (sizeof (midi_file_t)); + + ret->header.format_type = 0; + ret->header.num_tracks = 1; + ret->header.time_division = 10000; + ret->num_tracks = 1; + ret->buffer_size = 0; + ret->buffer = NULL; + ret->tracks = (midi_track_t*)malloc (sizeof (midi_track_t)); + + ret->tracks->num_events = 0; + ret->tracks->num_event_mem = 0; + ret->tracks->events = NULL; + + opi = MIDI_spmc (base, NULL, 20000); + + + while (1) + { + midi_event_t *oldev; + midi_event_t *nextev; + + if (ret->tracks->num_events == ret->tracks->num_event_mem) + { + ret->tracks->num_event_mem += 100; + ret->tracks->events = (midi_event_t*)realloc (ret->tracks->events, sizeof (midi_event_t) * ret->tracks->num_event_mem); + } + + oldev = flatlist[epos]; + nextev = ret->tracks->events + ret->tracks->num_events; + + + // figure delta time + nextev->delta_time = (unsigned int)(opi * oldev->delta_time); + + if (oldev->event_type == MIDI_EVENT_SYSEX || + oldev->event_type == MIDI_EVENT_SYSEX_SPLIT) + // opl player can't process any sysex... + { + epos++; + continue; + } + + if (oldev->event_type == MIDI_EVENT_META) + { + if (oldev->data.meta.type == MIDI_META_SET_TEMPO) + { // adjust future tempo scaling + opi = MIDI_spmc (base, oldev, 20000); + // insert event as dummy + nextev->event_type = MIDI_EVENT_META; + nextev->data.meta.type = MIDI_META_TEXT; + nextev->data.meta.length = 0; + nextev->data.meta.data = (byte*)malloc (4); + epos++; + ret->tracks->num_events++; + continue; + } + if (oldev->data.meta.type == MIDI_META_END_OF_TRACK) + { // reproduce event and break + nextev->event_type = MIDI_EVENT_META; + nextev->data.meta.type = MIDI_META_END_OF_TRACK; + nextev->data.meta.length = 0; + nextev->data.meta.data = (byte*)malloc (4); + epos++; + ret->tracks->num_events++; + break; + } + // other meta events not needed + epos++; + continue; + } + // non meta events can simply be copied (excluding delta time) + memcpy (&nextev->event_type, &oldev->event_type, sizeof (midi_event_t) - sizeof (unsigned)); + epos++; + ret->tracks->num_events++; + } + + MIDI_DestroyFlatList (flatlist); + MIDI_FreeFile (base); + return ret; +} + + + +#ifdef TEST + +static char *MIDI_EventTypeToString(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)"; + } +} + +void PrintTrack (midi_track_t *track) +{ + midi_event_t *event; + unsigned int i; + + for (i=0; inum_events; ++i) + { + event = &track->events[i]; + + if (event->delta_time > 0) + { + printf("Delay: %i ticks\n", event->delta_time); + } + + printf("Event type: %s (%i)\n", + MIDI_EventTypeToString(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: %i\n", event->data.channel.channel); + printf("\tParameter 1: %i\n", event->data.channel.param1); + printf("\tParameter 2: %i\n", event->data.channel.param2); + break; + + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + printf("\tLength: %i\n", event->data.sysex.length); + break; + + case MIDI_EVENT_META: + printf("\tMeta type: %i\n", event->data.meta.type); + printf("\tLength: %i\n", event->data.meta.length); + break; + } + } +} + +int main(int argc, char *argv[]) +{ + FILE *f; + midimem_t mf; + midi_file_t *file; + unsigned int i; + + if (argc < 2) + { + printf("Usage: %s \n", argv[0]); + exit(1); + } + f = M_fopen (argv[1], "rb"); + if (!f) + { + fprintf(stderr, "Failed to open %s\n", argv[1]); + exit(1); + } + fseek (f, 0, SEEK_END); + mf.len = ftell (f); + mf.pos = 0; + rewind (f); + mf.data = malloc (mf.len); + fread (mf.data, 1, mf.len, f); + fclose (f); + + file = MIDI_LoadFile (&mf); + + if (file == NULL) + { + fprintf(stderr, "Failed to open %s\n", argv[1]); + exit(1); + } + + for (i=0; inum_tracks; ++i) + { + printf("\n== Track %i ==\n\n", i); + + PrintTrack(&file->tracks[i]); + } + + return 0; +} + +#endif + diff --git a/src/MUSIC/midifile.h b/src/MUSIC/midifile.h new file mode 100644 index 0000000..ca4b6b3 --- /dev/null +++ b/src/MUSIC/midifile.h @@ -0,0 +1,194 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// MIDI file parsing. +// +//----------------------------------------------------------------------------- + +#ifndef MIDIFILE_H +#define MIDIFILE_H + +#include "doomtype.h" + +typedef struct midi_file_s midi_file_t; +typedef struct midi_track_iter_s midi_track_iter_t; + +#define MIDI_CHANNELS_PER_TRACK 16 + +typedef struct +{ + const byte *data; + size_t len; + size_t pos; +} midimem_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 = 0x0, + MIDI_CONTROLLER_MODULATION = 0x1, + MIDI_CONTROLLER_BREATH_CONTROL = 0x2, + MIDI_CONTROLLER_FOOT_CONTROL = 0x3, + MIDI_CONTROLLER_PORTAMENTO = 0x4, + MIDI_CONTROLLER_DATA_ENTRY = 0x5, + + MIDI_CONTROLLER_MAIN_VOLUME = 0x7, + MIDI_CONTROLLER_PAN = 0xa +} midi_controller_t; + +typedef enum +{ + MIDI_META_SEQUENCE_NUMBER = 0x0, + + MIDI_META_TEXT = 0x1, + MIDI_META_COPYRIGHT = 0x2, + MIDI_META_TRACK_NAME = 0x3, + MIDI_META_INSTR_NAME = 0x4, + MIDI_META_LYRICS = 0x5, + MIDI_META_MARKER = 0x6, + MIDI_META_CUE_POINT = 0x7, + + 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 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; + +// Load a MIDI file. + +midi_file_t *MIDI_LoadFile(midimem_t *mf); + +// Free a MIDI file. + +void MIDI_FreeFile(midi_file_t *file); + +// Get the time division value from the MIDI header. + +unsigned int MIDI_GetFileTimeDivision(const midi_file_t *file); + +// Get the number of tracks in a MIDI file. + +unsigned int MIDI_NumTracks(const midi_file_t *file); + +// Start iterating over the events in a track. + +midi_track_iter_t *MIDI_IterateTrack(const midi_file_t *file, unsigned int track_num); + +// Free an iterator. + +void MIDI_FreeIterator(midi_track_iter_t *iter); + +// Get the time until the next MIDI event in a track. + +unsigned int MIDI_GetDeltaTime(midi_track_iter_t *iter); + +// Get a pointer to the next MIDI event. + +int MIDI_GetNextEvent(midi_track_iter_t *iter, midi_event_t **event); + +// Reset an iterator to the beginning of a track. + +void MIDI_RestartIterator(midi_track_iter_t *iter); + +// NSM: an alternate iterator tool. +midi_event_t **MIDI_GenerateFlatList (midi_file_t *file); +void MIDI_DestroyFlatList (midi_event_t **evs); + +// NSM: timing calculator +double MIDI_spmc (const midi_file_t *file, const midi_event_t *ev, unsigned sndrate); + +midi_file_t *MIDI_LoadFileSpecial (midimem_t *mf); + +#endif /* #ifndef MIDIFILE_H */ + diff --git a/src/MUSIC/musicplayer.h b/src/MUSIC/musicplayer.h new file mode 100644 index 0000000..b6f40ed --- /dev/null +++ b/src/MUSIC/musicplayer.h @@ -0,0 +1,124 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * + * Copyright (C) 2011 by + * Nicholai Main + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + + +#ifndef MUSICPLAYER_H +#define MUSICPLAYER_H + +/* +Anything that implements all of these functions can play music in prboomplus. + +render: If audio output isn't delivered by render (say, for midi out), then the +player won't be recordable with video capture. In that case, still write 0s to +the buffer when render is called. + +thread safety: Locks are handled in i_sound.c. Don't worry about it. + +Timing: If you're outputting to render, your timing should come solely from the +calls to render, and not some external timing source. That's why things stay +synced. +*/ + + +typedef struct +{ + // descriptive name of the player, such as "OPL2 Synth" + const char *(*name)(void); + + // samplerate is in hz. return is 1 for success + int (*init)(int samplerate); + + // deallocate structures, cleanup, ... + void (*shutdown)(void); + + // set volume, 0 = off, 15 = max + void (*setvolume)(int v); + + // pause currently running song. + void (*pause)(void); + + // undo pause + void (*resume)(void); + + // return a player-specific handle, or NULL on failure. + // data does not belong to player, but it will persist as long as unregister is not called + const void *(*registersong)(const void *data, unsigned len); + + // deallocate structures, etc. data is no longer valid + void (*unregistersong)(const void *handle); + + void (*play)(const void *handle, int looping); + + // stop + void (*stop)(void); + + // s16 stereo, with samplerate as specified in init. player needs to be able to handle + // just about anything for nsamp. render can be called even during pause+stop. + void (*render)(void *dest, unsigned nsamp); +} music_player_t; + + + +// helper for deferred load dll + +#ifdef _MSC_VER +#if 1 +#define TESTDLLLOAD(a,b) +#else +#define TESTDLLLOAD(a,b) \ + if (1) \ + { \ + HMODULE h = LoadLibrary (a); \ + if (!h) \ + { \ + lprintf (LO_INFO, a " not found!\n"); \ + return 0; \ + } \ + FreeLibrary (h); \ + if (b && FAILED (__HrLoadAllImportsForDll (a))) \ + { \ + lprintf (LO_INFO, "Couldn't get all symbols from " a "\n"); \ + return 0; \ + } \ + } +#endif + +#else // _MSC_VER +#define TESTDLLLOAD(a,b) + +#endif // _MSC_VER + + + + + + + +#endif // MUSICPLAYER_H diff --git a/src/MUSIC/opl.c b/src/MUSIC/opl.c new file mode 100644 index 0000000..99548e6 --- /dev/null +++ b/src/MUSIC/opl.c @@ -0,0 +1,459 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// OPL interface. +// +//----------------------------------------------------------------------------- + + +#include "config.h" + +#include +#include + + +#include "opl.h" +#include "opl_queue.h" +#include "dbopl.h" + +#include "i_sound.h" // mus_opl_gain + +static int init_stage_reg_writes = 1; + +unsigned int opl_sample_rate = 22050; + + +#define MAX_SOUND_SLICE_TIME 100 /* ms */ + +typedef struct +{ + unsigned int rate; // Number of times the timer is advanced per sec. + unsigned int enabled; // Non-zero if timer is enabled. + unsigned int value; // Last value that was set. + unsigned int expire_time; // Calculated time that timer will expire. +} opl_timer_t; + + +// Queue of callbacks waiting to be invoked. + +static opl_callback_queue_t *callback_queue; + + +// Current time, in number of samples since startup: + +static unsigned int current_time; + +// If non-zero, playback is currently paused. + +static int opl_paused; + +// Time offset (in samples) due to the fact that callbacks +// were previously paused. + +static unsigned int pause_offset; + +// OPL software emulator structure. + +static Chip opl_chip; + +// Temporary mixing buffer used by the mixing callback. + +static int *mix_buffer = NULL; + +// Register number that was written. + +static int register_num = 0; + +// Timers; DBOPL does not do timer stuff itself. + +static opl_timer_t timer1 = { 12500, 0, 0, 0 }; +static opl_timer_t timer2 = { 3125, 0, 0, 0 }; + + +// +// Init/shutdown code. +// + +// Initialize the OPL library. Returns true if initialized +// successfully. + +int OPL_Init (unsigned int rate) +{ + opl_sample_rate = rate; + opl_paused = 0; + pause_offset = 0; + + // Queue structure of callbacks to invoke. + + callback_queue = OPL_Queue_Create(); + current_time = 0; + + + mix_buffer = (int*)malloc(opl_sample_rate * sizeof(int)); + + // Create the emulator structure: + + DBOPL_InitTables(); + Chip__Chip(&opl_chip); + Chip__Setup(&opl_chip, opl_sample_rate); + + + OPL_InitRegisters(); + + init_stage_reg_writes = 0; + + return 1; +} + +// Shut down the OPL library. + +void OPL_Shutdown(void) +{ + if (callback_queue) + { + OPL_Queue_Destroy(callback_queue); + free(mix_buffer); + + callback_queue = NULL; + mix_buffer = NULL; + } +} + +void OPL_SetCallback(unsigned int ms, + opl_callback_t callback, + void *data) +{ + OPL_Queue_Push(callback_queue, callback, data, + current_time - pause_offset + (ms * opl_sample_rate) / 1000); +} + +void OPL_ClearCallbacks(void) +{ + OPL_Queue_Clear(callback_queue); +} + +static void OPLTimer_CalculateEndTime(opl_timer_t *timer) +{ + int tics; + + // If the timer is enabled, calculate the time when the timer + // will expire. + + if (timer->enabled) + { + tics = 0x100 - timer->value; + timer->expire_time = current_time + + (tics * opl_sample_rate) / timer->rate; + } +} + + +static void WriteRegister(unsigned int reg_num, unsigned int value) +{ + switch (reg_num) + { + case OPL_REG_TIMER1: + timer1.value = value; + OPLTimer_CalculateEndTime(&timer1); + break; + + case OPL_REG_TIMER2: + timer2.value = value; + OPLTimer_CalculateEndTime(&timer2); + break; + + case OPL_REG_TIMER_CTRL: + if (value & 0x80) + { + timer1.enabled = 0; + timer2.enabled = 0; + } + else + { + if ((value & 0x40) == 0) + { + timer1.enabled = (value & 0x01) != 0; + OPLTimer_CalculateEndTime(&timer1); + } + + if ((value & 0x20) == 0) + { + timer1.enabled = (value & 0x02) != 0; + OPLTimer_CalculateEndTime(&timer2); + } + } + + break; + + default: + Chip__WriteReg(&opl_chip, reg_num, (unsigned char) value); + break; + } +} + +static void OPL_AdvanceTime(unsigned int nsamples) +{ + opl_callback_t callback; + void *callback_data; + + + // Advance time. + + current_time += nsamples; + + if (opl_paused) + { + pause_offset += nsamples; + } + + // Are there callbacks to invoke now? Keep invoking them + // until there are none more left. + + while (!OPL_Queue_IsEmpty(callback_queue) + && current_time >= OPL_Queue_Peek(callback_queue) + pause_offset) + { + // Pop the callback from the queue to invoke it. + + if (!OPL_Queue_Pop(callback_queue, &callback, &callback_data)) + { + break; + } + + + callback(callback_data); + + } + +} + +static void FillBuffer(int16_t *buffer, unsigned int nsamples) +{ + unsigned int i; + int sampval; + + // FIXME??? + //assert(nsamples < opl_sample_rate); + + Chip__GenerateBlock2(&opl_chip, nsamples, mix_buffer); + + // Mix into the destination buffer, doubling up into stereo. + + for (i=0; i 32767) + sampval = 32767; + else if (sampval < -32768) + sampval = -32768; + buffer[i * 2] = (int16_t) sampval; + buffer[i * 2 + 1] = (int16_t) sampval; + } +} + + +void OPL_Render_Samples (void *dest, unsigned buffer_len) +{ + unsigned int filled = 0; + + + short *buffer = (short *) dest; + + + // Repeatedly call the OPL emulator update function until the buffer is + // full. + + while (filled < buffer_len) + { + unsigned int next_callback_time; + unsigned int nsamples; + + + // Work out the time until the next callback waiting in + // the callback queue must be invoked. We can then fill the + // buffer with this many samples. + + if (opl_paused || OPL_Queue_IsEmpty(callback_queue)) + { + nsamples = buffer_len - filled; + } + else + { + next_callback_time = OPL_Queue_Peek(callback_queue) + pause_offset; + + nsamples = next_callback_time - current_time; + + if (nsamples > buffer_len - filled) + { + nsamples = buffer_len - filled; + } + } + + + // Add emulator output to buffer. + + FillBuffer(buffer + filled * 2, nsamples); + filled += nsamples; + + // Invoke callbacks for this point in time. + + OPL_AdvanceTime(nsamples); + } +} + +void OPL_WritePort(opl_port_t port, unsigned int value) +{ + if (port == OPL_REGISTER_PORT) + { + register_num = value; + } + else if (port == OPL_DATA_PORT) + { + WriteRegister(register_num, value); + } +} + +unsigned int OPL_ReadPort(opl_port_t port) +{ + unsigned int result = 0; + + if (timer1.enabled && current_time > timer1.expire_time) + { + result |= 0x80; // Either have expired + result |= 0x40; // Timer 1 has expired + } + + if (timer2.enabled && current_time > timer2.expire_time) + { + result |= 0x80; // Either have expired + result |= 0x20; // Timer 2 has expired + } + + return result; +} + +// +// Higher-level functions, based on the lower-level functions above +// (register write, etc). +// + +unsigned int OPL_ReadStatus(void) +{ + return OPL_ReadPort(OPL_REGISTER_PORT); +} + +// Write an OPL register value + +void OPL_WriteRegister(int reg, int value) +{ + int i; + + OPL_WritePort(OPL_REGISTER_PORT, reg); + + // For timing, read the register port six times after writing the + // register number to cause the appropriate delay + + for (i=0; i<6; ++i) + { + // An oddity of the Doom OPL code: at startup initialization, + // the spacing here is performed by reading from the register + // port; after initialization, the data port is read, instead. + + if (init_stage_reg_writes) + { + OPL_ReadPort(OPL_REGISTER_PORT); + } + else + { + OPL_ReadPort(OPL_DATA_PORT); + } + } + + OPL_WritePort(OPL_DATA_PORT, value); + + // Read the register port 24 times after writing the value to + // cause the appropriate delay + + for (i=0; i<24; ++i) + { + OPL_ReadStatus(); + } +} + + +// Initialize registers on startup + +void OPL_InitRegisters(void) +{ + int r; + + // Initialize level registers + + for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r) + { + OPL_WriteRegister(r, 0x3f); + } + + // Initialize other registers + // These two loops write to registers that actually don't exist, + // but this is what Doom does ... + // Similarly, the <= is also intenational. + + for (r=OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r) + { + OPL_WriteRegister(r, 0x00); + } + + // More registers ... + + for (r=1; r < OPL_REGS_LEVEL; ++r) + { + OPL_WriteRegister(r, 0x00); + } + + // Re-initialize the low registers: + + // Reset both timers and enable interrupts: + OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60); + OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80); + + // "Allow FM chips to control the waveform of each operator": + OPL_WriteRegister(OPL_REG_WAVEFORM_ENABLE, 0x20); + + // Keyboard split point on (?) + OPL_WriteRegister(OPL_REG_FM_MODE, 0x40); +} + + + +void OPL_SetPaused(int paused) +{ + opl_paused = paused; +} + + + + + + + + + diff --git a/src/MUSIC/opl.h b/src/MUSIC/opl.h new file mode 100644 index 0000000..d504023 --- /dev/null +++ b/src/MUSIC/opl.h @@ -0,0 +1,123 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// OPL interface. +// +//----------------------------------------------------------------------------- + + +#ifndef OPL_OPL_H +#define OPL_OPL_H + +typedef void (*opl_callback_t)(void *data); + +typedef enum +{ + OPL_REGISTER_PORT = 0, + OPL_DATA_PORT = 1 +} opl_port_t; + +#define OPL_NUM_OPERATORS 21 +#define OPL_NUM_VOICES 9 + +#define OPL_REG_WAVEFORM_ENABLE 0x01 +#define OPL_REG_TIMER1 0x02 +#define OPL_REG_TIMER2 0x03 +#define OPL_REG_TIMER_CTRL 0x04 +#define OPL_REG_FM_MODE 0x08 + +// Operator registers (21 of each): + +#define OPL_REGS_TREMOLO 0x20 +#define OPL_REGS_LEVEL 0x40 +#define OPL_REGS_ATTACK 0x60 +#define OPL_REGS_SUSTAIN 0x80 +#define OPL_REGS_WAVEFORM 0xE0 + +// Voice registers (9 of each): + +#define OPL_REGS_FREQ_1 0xA0 +#define OPL_REGS_FREQ_2 0xB0 +#define OPL_REGS_FEEDBACK 0xC0 + +// +// Low-level functions. +// + +// Initialize the OPL subsystem. + +int OPL_Init(unsigned int port_base); + +// Shut down the OPL subsystem. + +void OPL_Shutdown(void); + + +// Write to one of the OPL I/O ports: + +void OPL_WritePort(opl_port_t port, unsigned int value); + +// Read from one of the OPL I/O ports: + +unsigned int OPL_ReadPort(opl_port_t port); + +// +// Higher-level functions. +// + +// Read the cuurrent status byte of the OPL chip. + +unsigned int OPL_ReadStatus(void); + +// Write to an OPL register. + +void OPL_WriteRegister(int reg, int value); + +// Perform a detection sequence to determine that an +// OPL chip is present. + +int OPL_Detect(void); + +// Initialize all registers, performed on startup. + +void OPL_InitRegisters(void); + + +// Block until the specified number of milliseconds have elapsed. + +void OPL_Delay(unsigned int ms); + +// Pause the OPL callbacks. + +void OPL_SetPaused(int paused); + + +extern unsigned int opl_sample_rate; + +void OPL_Render_Samples (void *dest, unsigned nsamp); + + +void OPL_SetCallback(unsigned int ms, opl_callback_t callback, void *data); + +void OPL_ClearCallbacks(void); + +#endif + diff --git a/src/MUSIC/opl_queue.c b/src/MUSIC/opl_queue.c new file mode 100644 index 0000000..dc2b551 --- /dev/null +++ b/src/MUSIC/opl_queue.c @@ -0,0 +1,280 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// Queue of waiting callbacks, stored in a binary min heap, so that we +// can always get the first callback. +// +//----------------------------------------------------------------------------- + +#include +#include +#include "lprintf.h" + +#include "opl_queue.h" + +#define MAX_OPL_QUEUE 64 + +typedef struct +{ + opl_callback_t callback; + void *data; + unsigned int time; +} opl_queue_entry_t; + +struct opl_callback_queue_s +{ + opl_queue_entry_t entries[MAX_OPL_QUEUE]; + int num_entries; +}; + +opl_callback_queue_t *OPL_Queue_Create(void) +{ + opl_callback_queue_t *queue; + + queue = (opl_callback_queue_t*)malloc(sizeof(opl_callback_queue_t)); + queue->num_entries = 0; + + return queue; +} + +void OPL_Queue_Destroy(opl_callback_queue_t *queue) +{ + free(queue); +} + +int OPL_Queue_IsEmpty(opl_callback_queue_t *queue) +{ + return queue->num_entries == 0; +} + +void OPL_Queue_Clear(opl_callback_queue_t *queue) +{ + queue->num_entries = 0; +} + +void OPL_Queue_Push(opl_callback_queue_t *queue, + opl_callback_t callback, void *data, + unsigned int time) +{ + int entry_id; + int parent_id; + + if (queue->num_entries >= MAX_OPL_QUEUE) + { + lprintf (LO_WARN, "OPL_Queue_Push: Exceeded maximum callbacks\n"); + return; + } + + // Add to last queue entry. + + entry_id = queue->num_entries; + ++queue->num_entries; + + // Shift existing entries down in the heap. + + while (entry_id > 0) + { + parent_id = (entry_id - 1) / 2; + + // Is the heap condition satisfied? + + if (time >= queue->entries[parent_id].time) + { + break; + } + + // Move the existing entry down in the heap. + + memcpy(&queue->entries[entry_id], + &queue->entries[parent_id], + sizeof(opl_queue_entry_t)); + + // Advance to the parent. + + entry_id = parent_id; + } + + // Insert new callback data. + + queue->entries[entry_id].callback = callback; + queue->entries[entry_id].data = data; + queue->entries[entry_id].time = time; +} + +int OPL_Queue_Pop(opl_callback_queue_t *queue, + opl_callback_t *callback, void **data) +{ + opl_queue_entry_t *entry; + int child1, child2; + int i, next_i; + + // Empty? + + if (queue->num_entries <= 0) + { + return 0; + } + + // Store the result: + + *callback = queue->entries[0].callback; + *data = queue->entries[0].data; + + // Decrease the heap size, and keep pointer to the last entry in + // the heap, which must now be percolated down from the top. + + --queue->num_entries; + entry = &queue->entries[queue->num_entries]; + + // Percolate down. + + i = 0; + + for (;;) + { + child1 = i * 2 + 1; + child2 = i * 2 + 2; + + if (child1 < queue->num_entries + && queue->entries[child1].time < entry->time) + { + // Left child is less than entry. + // Use the minimum of left and right children. + + if (child2 < queue->num_entries + && queue->entries[child2].time < queue->entries[child1].time) + { + next_i = child2; + } + else + { + next_i = child1; + } + } + else if (child2 < queue->num_entries + && queue->entries[child2].time < entry->time) + { + // Right child is less than entry. Go down the right side. + + next_i = child2; + } + else + { + // Finished percolating. + break; + } + + // Percolate the next value up and advance. + + memcpy(&queue->entries[i], + &queue->entries[next_i], + sizeof(opl_queue_entry_t)); + i = next_i; + } + + // Store the old last-entry at its new position. + + memcpy(&queue->entries[i], entry, sizeof(opl_queue_entry_t)); + + return 1; +} + +unsigned int OPL_Queue_Peek(opl_callback_queue_t *queue) +{ + if (queue->num_entries > 0) + { + return queue->entries[0].time; + } + else + { + return 0; + } +} + +#ifdef TEST + +#include + +static void PrintQueueNode(opl_callback_queue_t *queue, int node, int depth) +{ + int i; + + if (node >= queue->num_entries) + { + return; + } + + for (i=0; ientries[node].time); + + PrintQueueNode(queue, node * 2 + 1, depth + 1); + PrintQueueNode(queue, node * 2 + 2, depth + 1); +} + +static void PrintQueue(opl_callback_queue_t *queue) +{ + PrintQueueNode(queue, 0, 0); +} + +int main() +{ + opl_callback_queue_t *queue; + int iteration; + + queue = OPL_Queue_Create(); + + for (iteration=0; iteration<5000; ++iteration) + { + opl_callback_t callback; + void *data; + unsigned int time; + unsigned int newtime; + int i; + + for (i=0; i= time); + time = newtime; + } + + assert(OPL_Queue_IsEmpty(queue)); + assert(!OPL_Queue_Pop(queue, &callback, &data)); + } +} + +#endif + diff --git a/src/MUSIC/opl_queue.h b/src/MUSIC/opl_queue.h new file mode 100644 index 0000000..2447702 --- /dev/null +++ b/src/MUSIC/opl_queue.h @@ -0,0 +1,45 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// OPL callback queue. +// +//----------------------------------------------------------------------------- + +#ifndef OPL_QUEUE_H +#define OPL_QUEUE_H + +#include "opl.h" + +typedef struct opl_callback_queue_s opl_callback_queue_t; + +opl_callback_queue_t *OPL_Queue_Create(void); +int OPL_Queue_IsEmpty(opl_callback_queue_t *queue); +void OPL_Queue_Clear(opl_callback_queue_t *queue); +void OPL_Queue_Destroy(opl_callback_queue_t *queue); +void OPL_Queue_Push(opl_callback_queue_t *queue, + opl_callback_t callback, void *data, + unsigned int time); +int OPL_Queue_Pop(opl_callback_queue_t *queue, + opl_callback_t *callback, void **data); +unsigned int OPL_Queue_Peek(opl_callback_queue_t *queue); + +#endif /* #ifndef OPL_QUEUE_H */ + diff --git a/src/MUSIC/oplplayer.c b/src/MUSIC/oplplayer.c new file mode 100644 index 0000000..bdfcc19 --- /dev/null +++ b/src/MUSIC/oplplayer.c @@ -0,0 +1,1448 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// System interface for music. +// +//----------------------------------------------------------------------------- + +#include +#include +#include + +#include "doomdef.h" +#include "memio.h" +#include "mus2mid.h" + +#include "m_misc.h" +#include "s_sound.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "opl.h" +#include "midifile.h" + +#include "musicplayer.h" + +#include "lprintf.h" + +// #define OPL_MIDI_DEBUG + +#define MAXMIDLENGTH (96 * 1024) +#define GENMIDI_NUM_INSTRS 128 + +#define GENMIDI_HEADER "#OPL_II#" +#define GENMIDI_FLAG_FIXED 0x0001 /* fixed pitch */ +#define GENMIDI_FLAG_2VOICE 0x0004 /* double voice (OPL3) */ + +typedef struct +{ + byte tremolo; + byte attack; + byte sustain; + byte waveform; + byte scale; + byte level; +} PACKEDATTR genmidi_op_t; + +typedef struct +{ + genmidi_op_t modulator; + byte feedback; + genmidi_op_t carrier; + byte unused; + short base_note_offset; +} PACKEDATTR genmidi_voice_t; + +typedef struct +{ + unsigned short flags; + byte fine_tuning; + byte fixed_note; + + genmidi_voice_t voices[2]; +} PACKEDATTR genmidi_instr_t; + +// Data associated with a channel of a track that is currently playing. + +typedef struct +{ + // The instrument currently used for this track. + + const genmidi_instr_t *instrument; + + // Volume level + + int volume; + + // Pitch bend value: + + int bend; + +} opl_channel_data_t; + +// Data associated with a track that is currently playing. + +typedef struct +{ + // Data for each channel. + + opl_channel_data_t channels[MIDI_CHANNELS_PER_TRACK]; + + // Track iterator used to read new events. + + midi_track_iter_t *iter; + + // Tempo control variables + + unsigned int ticks_per_beat; + unsigned int ms_per_beat; +} opl_track_data_t; + +typedef struct opl_voice_s opl_voice_t; + +struct opl_voice_s +{ + // Index of this voice: + int index; + + // The operators used by this voice: + int op1, op2; + + // Currently-loaded instrument data + const genmidi_instr_t *current_instr; + + // The voice number in the instrument to use. + // This is normally set to zero; if this is a double voice + // instrument, it may be one. + unsigned int current_instr_voice; + + // The channel currently using this voice. + opl_channel_data_t *channel; + + // The midi key that this voice is playing. + unsigned int key; + + // The note being played. This is normally the same as + // the key, but if the instrument is a fixed pitch + // instrument, it is different. + unsigned int note; + + // The frequency value being used. + unsigned int freq; + + // The volume of the note being played on this channel. + unsigned int note_volume; + + // The current volume (register value) that has been set for this channel. + unsigned int reg_volume; + + // Next in linked list; a voice is always either in the + // free list or the allocated list. + opl_voice_t *next; +}; + +// Operators used by the different voices. + +static const int voice_operators[2][OPL_NUM_VOICES] = { + { 0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12 }, + { 0x03, 0x04, 0x05, 0x0b, 0x0c, 0x0d, 0x13, 0x14, 0x15 } +}; + +// Frequency values to use for each note. + +static const unsigned short frequency_curve[] = { + + 0x133, 0x133, 0x134, 0x134, 0x135, 0x136, 0x136, 0x137, // -1 + 0x137, 0x138, 0x138, 0x139, 0x139, 0x13a, 0x13b, 0x13b, + 0x13c, 0x13c, 0x13d, 0x13d, 0x13e, 0x13f, 0x13f, 0x140, + 0x140, 0x141, 0x142, 0x142, 0x143, 0x143, 0x144, 0x144, + + 0x145, 0x146, 0x146, 0x147, 0x147, 0x148, 0x149, 0x149, // -2 + 0x14a, 0x14a, 0x14b, 0x14c, 0x14c, 0x14d, 0x14d, 0x14e, + 0x14f, 0x14f, 0x150, 0x150, 0x151, 0x152, 0x152, 0x153, + 0x153, 0x154, 0x155, 0x155, 0x156, 0x157, 0x157, 0x158, + + // These are used for the first seven MIDI note values: + + 0x158, 0x159, 0x15a, 0x15a, 0x15b, 0x15b, 0x15c, 0x15d, // 0 + 0x15d, 0x15e, 0x15f, 0x15f, 0x160, 0x161, 0x161, 0x162, + 0x162, 0x163, 0x164, 0x164, 0x165, 0x166, 0x166, 0x167, + 0x168, 0x168, 0x169, 0x16a, 0x16a, 0x16b, 0x16c, 0x16c, + + 0x16d, 0x16e, 0x16e, 0x16f, 0x170, 0x170, 0x171, 0x172, // 1 + 0x172, 0x173, 0x174, 0x174, 0x175, 0x176, 0x176, 0x177, + 0x178, 0x178, 0x179, 0x17a, 0x17a, 0x17b, 0x17c, 0x17c, + 0x17d, 0x17e, 0x17e, 0x17f, 0x180, 0x181, 0x181, 0x182, + + 0x183, 0x183, 0x184, 0x185, 0x185, 0x186, 0x187, 0x188, // 2 + 0x188, 0x189, 0x18a, 0x18a, 0x18b, 0x18c, 0x18d, 0x18d, + 0x18e, 0x18f, 0x18f, 0x190, 0x191, 0x192, 0x192, 0x193, + 0x194, 0x194, 0x195, 0x196, 0x197, 0x197, 0x198, 0x199, + + 0x19a, 0x19a, 0x19b, 0x19c, 0x19d, 0x19d, 0x19e, 0x19f, // 3 + 0x1a0, 0x1a0, 0x1a1, 0x1a2, 0x1a3, 0x1a3, 0x1a4, 0x1a5, + 0x1a6, 0x1a6, 0x1a7, 0x1a8, 0x1a9, 0x1a9, 0x1aa, 0x1ab, + 0x1ac, 0x1ad, 0x1ad, 0x1ae, 0x1af, 0x1b0, 0x1b0, 0x1b1, + + 0x1b2, 0x1b3, 0x1b4, 0x1b4, 0x1b5, 0x1b6, 0x1b7, 0x1b8, // 4 + 0x1b8, 0x1b9, 0x1ba, 0x1bb, 0x1bc, 0x1bc, 0x1bd, 0x1be, + 0x1bf, 0x1c0, 0x1c0, 0x1c1, 0x1c2, 0x1c3, 0x1c4, 0x1c4, + 0x1c5, 0x1c6, 0x1c7, 0x1c8, 0x1c9, 0x1c9, 0x1ca, 0x1cb, + + 0x1cc, 0x1cd, 0x1ce, 0x1ce, 0x1cf, 0x1d0, 0x1d1, 0x1d2, // 5 + 0x1d3, 0x1d3, 0x1d4, 0x1d5, 0x1d6, 0x1d7, 0x1d8, 0x1d8, + 0x1d9, 0x1da, 0x1db, 0x1dc, 0x1dd, 0x1de, 0x1de, 0x1df, + 0x1e0, 0x1e1, 0x1e2, 0x1e3, 0x1e4, 0x1e5, 0x1e5, 0x1e6, + + 0x1e7, 0x1e8, 0x1e9, 0x1ea, 0x1eb, 0x1ec, 0x1ed, 0x1ed, // 6 + 0x1ee, 0x1ef, 0x1f0, 0x1f1, 0x1f2, 0x1f3, 0x1f4, 0x1f5, + 0x1f6, 0x1f6, 0x1f7, 0x1f8, 0x1f9, 0x1fa, 0x1fb, 0x1fc, + 0x1fd, 0x1fe, 0x1ff, 0x200, 0x201, 0x201, 0x202, 0x203, + + // First note of looped range used for all octaves: + + 0x204, 0x205, 0x206, 0x207, 0x208, 0x209, 0x20a, 0x20b, // 7 + 0x20c, 0x20d, 0x20e, 0x20f, 0x210, 0x210, 0x211, 0x212, + 0x213, 0x214, 0x215, 0x216, 0x217, 0x218, 0x219, 0x21a, + 0x21b, 0x21c, 0x21d, 0x21e, 0x21f, 0x220, 0x221, 0x222, + + 0x223, 0x224, 0x225, 0x226, 0x227, 0x228, 0x229, 0x22a, // 8 + 0x22b, 0x22c, 0x22d, 0x22e, 0x22f, 0x230, 0x231, 0x232, + 0x233, 0x234, 0x235, 0x236, 0x237, 0x238, 0x239, 0x23a, + 0x23b, 0x23c, 0x23d, 0x23e, 0x23f, 0x240, 0x241, 0x242, + + 0x244, 0x245, 0x246, 0x247, 0x248, 0x249, 0x24a, 0x24b, // 9 + 0x24c, 0x24d, 0x24e, 0x24f, 0x250, 0x251, 0x252, 0x253, + 0x254, 0x256, 0x257, 0x258, 0x259, 0x25a, 0x25b, 0x25c, + 0x25d, 0x25e, 0x25f, 0x260, 0x262, 0x263, 0x264, 0x265, + + 0x266, 0x267, 0x268, 0x269, 0x26a, 0x26c, 0x26d, 0x26e, // 10 + 0x26f, 0x270, 0x271, 0x272, 0x273, 0x275, 0x276, 0x277, + 0x278, 0x279, 0x27a, 0x27b, 0x27d, 0x27e, 0x27f, 0x280, + 0x281, 0x282, 0x284, 0x285, 0x286, 0x287, 0x288, 0x289, + + 0x28b, 0x28c, 0x28d, 0x28e, 0x28f, 0x290, 0x292, 0x293, // 11 + 0x294, 0x295, 0x296, 0x298, 0x299, 0x29a, 0x29b, 0x29c, + 0x29e, 0x29f, 0x2a0, 0x2a1, 0x2a2, 0x2a4, 0x2a5, 0x2a6, + 0x2a7, 0x2a9, 0x2aa, 0x2ab, 0x2ac, 0x2ae, 0x2af, 0x2b0, + + 0x2b1, 0x2b2, 0x2b4, 0x2b5, 0x2b6, 0x2b7, 0x2b9, 0x2ba, // 12 + 0x2bb, 0x2bd, 0x2be, 0x2bf, 0x2c0, 0x2c2, 0x2c3, 0x2c4, + 0x2c5, 0x2c7, 0x2c8, 0x2c9, 0x2cb, 0x2cc, 0x2cd, 0x2ce, + 0x2d0, 0x2d1, 0x2d2, 0x2d4, 0x2d5, 0x2d6, 0x2d8, 0x2d9, + + 0x2da, 0x2dc, 0x2dd, 0x2de, 0x2e0, 0x2e1, 0x2e2, 0x2e4, // 13 + 0x2e5, 0x2e6, 0x2e8, 0x2e9, 0x2ea, 0x2ec, 0x2ed, 0x2ee, + 0x2f0, 0x2f1, 0x2f2, 0x2f4, 0x2f5, 0x2f6, 0x2f8, 0x2f9, + 0x2fb, 0x2fc, 0x2fd, 0x2ff, 0x300, 0x302, 0x303, 0x304, + + 0x306, 0x307, 0x309, 0x30a, 0x30b, 0x30d, 0x30e, 0x310, // 14 + 0x311, 0x312, 0x314, 0x315, 0x317, 0x318, 0x31a, 0x31b, + 0x31c, 0x31e, 0x31f, 0x321, 0x322, 0x324, 0x325, 0x327, + 0x328, 0x329, 0x32b, 0x32c, 0x32e, 0x32f, 0x331, 0x332, + + 0x334, 0x335, 0x337, 0x338, 0x33a, 0x33b, 0x33d, 0x33e, // 15 + 0x340, 0x341, 0x343, 0x344, 0x346, 0x347, 0x349, 0x34a, + 0x34c, 0x34d, 0x34f, 0x350, 0x352, 0x353, 0x355, 0x357, + 0x358, 0x35a, 0x35b, 0x35d, 0x35e, 0x360, 0x361, 0x363, + + 0x365, 0x366, 0x368, 0x369, 0x36b, 0x36c, 0x36e, 0x370, // 16 + 0x371, 0x373, 0x374, 0x376, 0x378, 0x379, 0x37b, 0x37c, + 0x37e, 0x380, 0x381, 0x383, 0x384, 0x386, 0x388, 0x389, + 0x38b, 0x38d, 0x38e, 0x390, 0x392, 0x393, 0x395, 0x397, + + 0x398, 0x39a, 0x39c, 0x39d, 0x39f, 0x3a1, 0x3a2, 0x3a4, // 17 + 0x3a6, 0x3a7, 0x3a9, 0x3ab, 0x3ac, 0x3ae, 0x3b0, 0x3b1, + 0x3b3, 0x3b5, 0x3b7, 0x3b8, 0x3ba, 0x3bc, 0x3bd, 0x3bf, + 0x3c1, 0x3c3, 0x3c4, 0x3c6, 0x3c8, 0x3ca, 0x3cb, 0x3cd, + + // The last note has an incomplete range, and loops round back to + // the start. Note that the last value is actually a buffer overrun + // and does not fit with the other values. + + 0x3cf, 0x3d1, 0x3d2, 0x3d4, 0x3d6, 0x3d8, 0x3da, 0x3db, // 18 + 0x3dd, 0x3df, 0x3e1, 0x3e3, 0x3e4, 0x3e6, 0x3e8, 0x3ea, + 0x3ec, 0x3ed, 0x3ef, 0x3f1, 0x3f3, 0x3f5, 0x3f6, 0x3f8, + 0x3fa, 0x3fc, 0x3fe, 0x36c, +}; + +// Mapping from MIDI volume level to OPL level value. + +static const unsigned int volume_mapping_table[] = { + 0, 1, 3, 5, 6, 8, 10, 11, + 13, 14, 16, 17, 19, 20, 22, 23, + 25, 26, 27, 29, 30, 32, 33, 34, + 36, 37, 39, 41, 43, 45, 47, 49, + 50, 52, 54, 55, 57, 59, 60, 61, + 63, 64, 66, 67, 68, 69, 71, 72, + 73, 74, 75, 76, 77, 79, 80, 81, + 82, 83, 84, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 92, 93, 94, 95, + 96, 96, 97, 98, 99, 99, 100, 101, + 101, 102, 103, 103, 104, 105, 105, 106, + 107, 107, 108, 109, 109, 110, 110, 111, + 112, 112, 113, 113, 114, 114, 115, 115, + 116, 117, 117, 118, 118, 119, 119, 120, + 120, 121, 121, 122, 122, 123, 123, 123, + 124, 124, 125, 125, 126, 126, 127, 127 +}; + +static dboolean music_initialized = false; + +//static dboolean musicpaused = false; +static int current_music_volume; + +// GENMIDI lump instrument data: + +static const genmidi_instr_t *main_instrs; +static const genmidi_instr_t *percussion_instrs; + +// Voices: + +static opl_voice_t voices[OPL_NUM_VOICES]; +static opl_voice_t *voice_free_list; +static opl_voice_t *voice_alloced_list; + +// Track data for playing tracks: + +static opl_track_data_t *tracks; +static unsigned int num_tracks = 0; +static unsigned int running_tracks = 0; +static dboolean song_looping; + +// Configuration file variable, containing the port number for the +// adlib chip. + +int opl_io_port = 0x388; + +// Load instrument table from GENMIDI lump: + +static dboolean LoadInstrumentTable(void) +{ + const byte *lump; + + lump = (const byte*)W_CacheLumpName("GENMIDI"); + + // Check header + + if (strncmp((const char *) lump, GENMIDI_HEADER, strlen(GENMIDI_HEADER)) != 0) + { + W_UnlockLumpName("GENMIDI"); + + return false; + } + + main_instrs = (const genmidi_instr_t *) (lump + strlen(GENMIDI_HEADER)); + percussion_instrs = main_instrs + GENMIDI_NUM_INSTRS; + + return true; +} + +// Get the next available voice from the freelist. + +static opl_voice_t *GetFreeVoice(void) +{ + opl_voice_t *result; + + // None available? + + if (voice_free_list == NULL) + { + return NULL; + } + + // Remove from free list + + result = voice_free_list; + voice_free_list = voice_free_list->next; + + // Add to allocated list + + result->next = voice_alloced_list; + voice_alloced_list = result; + + return result; +} + +// Remove a voice from the allocated voices list. + +static void RemoveVoiceFromAllocedList(opl_voice_t *voice) +{ + opl_voice_t **rover; + + rover = &voice_alloced_list; + + // Search the list until we find the voice, then remove it. + + while (*rover != NULL) + { + if (*rover == voice) + { + *rover = voice->next; + voice->next = NULL; + break; + } + + rover = &(*rover)->next; + } +} + +// Release a voice back to the freelist. + +static void ReleaseVoice(opl_voice_t *voice) +{ + opl_voice_t **rover; + + voice->channel = NULL; + voice->note = 0; + + // Remove from alloced list. + + RemoveVoiceFromAllocedList(voice); + + // Search to the end of the freelist (This is how Doom behaves!) + + rover = &voice_free_list; + + while (*rover != NULL) + { + rover = &(*rover)->next; + } + + *rover = voice; + voice->next = NULL; +} + +// Load data to the specified Operator + +static void LoadOperatorData(int Operator, + const genmidi_op_t *data, + dboolean max_level) +{ + int level; + + // The scale and level fields must be combined for the level register. + // For the carrier wave we always set the maximum level. + + level = (data->scale & 0xc0) | (data->level & 0x3f); + + if (max_level) + { + level |= 0x3f; + } + + OPL_WriteRegister(OPL_REGS_LEVEL + Operator, level); + OPL_WriteRegister(OPL_REGS_TREMOLO + Operator, data->tremolo); + OPL_WriteRegister(OPL_REGS_ATTACK + Operator, data->attack); + OPL_WriteRegister(OPL_REGS_SUSTAIN + Operator, data->sustain); + OPL_WriteRegister(OPL_REGS_WAVEFORM + Operator, data->waveform); +} + +// Set the instrument for a particular voice. + +static void SetVoiceInstrument(opl_voice_t *voice, + const genmidi_instr_t *instr, + unsigned int instr_voice) +{ + const genmidi_voice_t *data; + unsigned int modulating; + + // Instrument already set for this channel? + + if (voice->current_instr == instr + && voice->current_instr_voice == instr_voice) + { + return; + } + + voice->current_instr = instr; + voice->current_instr_voice = instr_voice; + + data = &instr->voices[instr_voice]; + + // Are we usind modulated feedback mode? + + modulating = (data->feedback & 0x01) == 0; + + // Doom loads the second operator first, then the first. + // The carrier is set to minimum volume until the voice volume + // is set in SetVoiceVolume (below). If we are not using + // modulating mode, we must set both to minimum volume. + + LoadOperatorData(voice->op2, &data->carrier, true); + LoadOperatorData(voice->op1, &data->modulator, !modulating); + + // Set feedback register that control the connection between the + // two operators. Turn on bits in the upper nybble; I think this + // is for OPL3, where it turns on channel A/B. + + OPL_WriteRegister(OPL_REGS_FEEDBACK + voice->index, + data->feedback | 0x30); + + // Hack to force a volume update. + + voice->reg_volume = 999; +} + +static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) +{ + const genmidi_voice_t *opl_voice; + unsigned int full_volume; + unsigned int op_volume; + unsigned int reg_volume; + + voice->note_volume = volume; + + opl_voice = &voice->current_instr->voices[voice->current_instr_voice]; + + // Multiply note volume and channel volume to get the actual volume. + + full_volume = (volume_mapping_table[voice->note_volume] + * volume_mapping_table[voice->channel->volume] + * volume_mapping_table[current_music_volume]) / (127 * 127); + + // The volume of each instrument can be controlled via GENMIDI: + + op_volume = 0x3f - opl_voice->carrier.level; + + // The volume value to use in the register: + + reg_volume = (op_volume * full_volume) / 128; + reg_volume = (0x3f - reg_volume) | opl_voice->carrier.scale; + + // Update the volume register(s) if necessary. + + if (reg_volume != voice->reg_volume) + { + voice->reg_volume = reg_volume; + + OPL_WriteRegister(OPL_REGS_LEVEL + voice->op2, reg_volume); + + // If we are using non-modulated feedback mode, we must set the + // volume for both voices. + // Note that the same register volume value is written for + // both voices, always calculated from the carrier's level + // value. + + if ((opl_voice->feedback & 0x01) != 0) + { + OPL_WriteRegister(OPL_REGS_LEVEL + voice->op1, reg_volume); + } + } +} + +// Initialize the voice table and freelist + +static void InitVoices(void) +{ + int i; + + // Start with an empty free list. + + voice_free_list = NULL; + + // Initialize each voice. + + for (i=0; iindex, voice->freq >> 8); +} + +// Get the frequency that we should be using for a voice. + +static void KeyOffEvent(opl_track_data_t *track, midi_event_t *event) +{ + opl_channel_data_t *channel; + unsigned int key; + unsigned int i; + + /* + printf("note off: channel %i, %i, %i\n", + event->data.channel.channel, + event->data.channel.param1, + event->data.channel.param2); + */ + + channel = &track->channels[event->data.channel.channel]; + key = event->data.channel.param1; + + // Turn off voices being used to play this key. + // If it is a double voice instrument there will be two. + + for (i=0; inext) + { + if (rover->current_instr_voice != 0 + || (rover->channel > channel + && CompareChannelPriorities(channel, rover->channel) > 0)) + { + result = rover; + break; + } + } + + // If we didn't find a voice, find an existing voice being used to + // play a note on the same channel, and use that. + + if (result == NULL) + { + for (rover = voice_alloced_list; rover != NULL; rover = rover->next) + { + if (rover->channel == channel) + { + result = rover; + break; + } + } + } + + // Still nothing found? Give up and just use the first voice in + // the list. + + if (result == NULL) + { + result = voice_alloced_list; + } + + // Stop playing this voice playing and release it back to the free + // list. + + VoiceKeyOff(result); + ReleaseVoice(result); + + // Re-allocate the voice again and return it. + + return GetFreeVoice(); +} + + +static unsigned int FrequencyForVoice(opl_voice_t *voice) +{ + const genmidi_voice_t *gm_voice; + unsigned int freq_index; + unsigned int octave; + unsigned int sub_index; + unsigned int note; + + note = voice->note; + + // Apply note offset. + // Don't apply offset if the instrument is a fixed note instrument. + + gm_voice = &voice->current_instr->voices[voice->current_instr_voice]; + + if ((voice->current_instr->flags & GENMIDI_FLAG_FIXED) == 0) + { + note += (signed short) doom_htows(gm_voice->base_note_offset); + } + + // Avoid possible overflow due to base note offset: + + if (note > 0x7f) + { + note = voice->note; + } + + freq_index = 64 + 32 * note + voice->channel->bend; + + // If this is the second voice of a double voice instrument, the + // frequency index can be adjusted by the fine tuning field. + + if (voice->current_instr_voice != 0) + { + freq_index += (voice->current_instr->fine_tuning / 2) - 64; + } + + // The first 7 notes use the start of the table, while + // consecutive notes loop around the latter part. + + if (freq_index < 284) + { + return frequency_curve[freq_index]; + } + + sub_index = (freq_index - 284) % (12 * 32); + octave = (freq_index - 284) / (12 * 32); + + // Once the seventh octave is reached, things break down. + // We can only go up to octave 7 as a maximum anyway (the OPL + // register only has three bits for octave number), but for the + // notes in octave 7, the first five bits have octave=7, the + // following notes have octave=6. This 7/6 pattern repeats in + // following octaves (which are technically impossible to + // represent anyway). + + if (octave >= 7) + { + if (sub_index < 5) + { + octave = 7; + } + else + { + octave = 6; + } + } + + // Calculate the resulting register value to use for the frequency. + + return frequency_curve[sub_index + 284] | (octave << 10); +} + +// Update the frequency that a voice is programmed to use. + +static void UpdateVoiceFrequency(opl_voice_t *voice) +{ + unsigned int freq; + + // Calculate the frequency to use for this voice and update it + // if neccessary. + + freq = FrequencyForVoice(voice); + + if (voice->freq != freq) + { + OPL_WriteRegister(OPL_REGS_FREQ_1 + voice->index, freq & 0xff); + OPL_WriteRegister(OPL_REGS_FREQ_2 + voice->index, (freq >> 8) | 0x20); + + voice->freq = freq; + } +} + +// Program a single voice for an instrument. For a double voice +// instrument (GENMIDI_FLAG_2VOICE), this is called twice for each +// key on event. + +static void VoiceKeyOn(opl_channel_data_t *channel, + const genmidi_instr_t *instrument, + unsigned int instrument_voice, + unsigned int key, + unsigned int volume) +{ + opl_voice_t *voice; + + // Find a voice to use for this new note. + + voice = GetFreeVoice(); + + // If there are no more voices left, we must decide what to do. + // If this is the first voice of the instrument, free an existing + // voice and use that. Otherwise, if this is the second voice, + // it isn't as important; just discard it. + + if (voice == NULL) + { + if (instrument_voice == 0) + { + voice = ReplaceExistingVoice(channel); + } + else + { + return; + } + } + + voice->channel = channel; + voice->key = key; + + // Work out the note to use. This is normally the same as + // the key, unless it is a fixed pitch instrument. + + if ((instrument->flags & GENMIDI_FLAG_FIXED) != 0) + { + voice->note = instrument->fixed_note; + } + else + { + voice->note = key; + } + + // Program the voice with the instrument data: + + SetVoiceInstrument(voice, instrument, instrument_voice); + + // Set the volume level. + + SetVoiceVolume(voice, volume); + + // Write the frequency value to turn the note on. + + voice->freq = 0; + UpdateVoiceFrequency(voice); +} + +static void KeyOnEvent(opl_track_data_t *track, midi_event_t *event) +{ + const genmidi_instr_t *instrument; + opl_channel_data_t *channel; + unsigned int key; + unsigned int volume; + + /* + printf("note on: channel %i, %i, %i\n", + event->data.channel.channel, + event->data.channel.param1, + event->data.channel.param2); + */ + + if (event->data.channel.param2 == 0) + { // NSM + // i have no idea why this is the case, but it is + // note that you don't see this in any of the base doom/doom2 music + KeyOffEvent (track, event); + return; + } + + // The channel. + + channel = &track->channels[event->data.channel.channel]; + key = event->data.channel.param1; + volume = event->data.channel.param2; + + // Percussion channel (10) is treated differently. + + if (event->data.channel.channel == 9) + { + if (key < 35 || key > 81) + { + return; + } + + instrument = &percussion_instrs[key - 35]; + } + else + { + instrument = channel->instrument; + } + + // Find and program a voice for this instrument. If this + // is a double voice instrument, we must do this twice. + + VoiceKeyOn(channel, instrument, 0, key, volume); + + if ((instrument->flags & GENMIDI_FLAG_2VOICE) != 0) + { + VoiceKeyOn(channel, instrument, 1, key, volume); + } +} + +static void ProgramChangeEvent(opl_track_data_t *track, midi_event_t *event) +{ + int channel; + int instrument; + + // Set the instrument used on this channel. + + channel = event->data.channel.channel; + instrument = event->data.channel.param1; + track->channels[channel].instrument = &main_instrs[instrument]; + + // TODO: Look through existing voices that are turned on on this + // channel, and change the instrument. +} + +static void SetChannelVolume(opl_channel_data_t *channel, unsigned int volume) +{ + unsigned int i; + + channel->volume = volume; + + // Update all voices that this channel is using. + + for (i=0; idata.channel.channel, + event->data.channel.param1, + event->data.channel.param2); + */ + + channel = &track->channels[event->data.channel.channel]; + controller = event->data.channel.param1; + param = event->data.channel.param2; + + switch (controller) + { + case MIDI_CONTROLLER_MAIN_VOLUME: + SetChannelVolume(channel, param); + break; + + default: +#ifdef OPL_MIDI_DEBUG + lprintf (LO_WARN, "Unknown MIDI controller type: %i\n", controller); +#endif + break; + } +} + +// Process a pitch bend event. + +static void PitchBendEvent(opl_track_data_t *track, midi_event_t *event) +{ + opl_channel_data_t *channel; + unsigned int i; + + // Update the channel bend value. Only the MSB of the pitch bend + // value is considered: this is what Doom does. + + channel = &track->channels[event->data.channel.channel]; + channel->bend = event->data.channel.param2 - 64; + + // Update all voices for this channel. + + for (i=0; idata.meta.type) + { + // Things we can just ignore. + + case MIDI_META_SEQUENCE_NUMBER: + case MIDI_META_TEXT: + case MIDI_META_COPYRIGHT: + case MIDI_META_TRACK_NAME: + case MIDI_META_INSTR_NAME: + case MIDI_META_LYRICS: + case MIDI_META_MARKER: + case MIDI_META_CUE_POINT: + case MIDI_META_SEQUENCER_SPECIFIC: + break; + + // End of track - actually handled when we run out of events + // in the track, see below. + + case MIDI_META_END_OF_TRACK: + break; + + default: +#ifdef OPL_MIDI_DEBUG + lprintf (LO_WARN, "Unknown MIDI meta event type: %i\n", + event->data.meta.type); +#endif + break; + } +} + +// Process a MIDI event from a track. + +static void ProcessEvent(opl_track_data_t *track, midi_event_t *event) +{ + switch (event->event_type) + { + case MIDI_EVENT_NOTE_OFF: + KeyOffEvent(track, event); + break; + + case MIDI_EVENT_NOTE_ON: + KeyOnEvent(track, event); + break; + + case MIDI_EVENT_CONTROLLER: + ControllerEvent(track, event); + break; + + case MIDI_EVENT_PROGRAM_CHANGE: + ProgramChangeEvent(track, event); + break; + + case MIDI_EVENT_PITCH_BEND: + PitchBendEvent(track, event); + break; + + case MIDI_EVENT_META: + MetaEvent(track, event); + break; + + // SysEx events can be ignored. + + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + break; + + default: +#ifdef OPL_MIDI_DEBUG + lprintf (LO_WARN, "Unknown MIDI event type %i\n", event->event_type); +#endif + break; + } +} + +static void ScheduleTrack(opl_track_data_t *track); + +// Restart a song from the beginning. + +static void RestartSong(void) +{ + unsigned int i; + + running_tracks = num_tracks; + + // fix buggy songs that forget to terminate notes held over loop point + // sdl_mixer does this as well + for (i=0; iiter, &event)) + { + return; + } + + ProcessEvent(track, event); + + // End of track? + + if (event->event_type == MIDI_EVENT_META + && event->data.meta.type == MIDI_META_END_OF_TRACK) + { + --running_tracks; + + // When all tracks have finished, restart the song. + + if (running_tracks <= 0 && song_looping) + { + RestartSong(); + } + + return; + } + + // Reschedule the callback for the next event in the track. + + ScheduleTrack(track); +} + +static void ScheduleTrack(opl_track_data_t *track) +{ + unsigned int nticks; + unsigned int ms; + static int total = 0; + + // Get the number of milliseconds until the next event. + + nticks = MIDI_GetDeltaTime(track->iter); + ms = (nticks * track->ms_per_beat) / track->ticks_per_beat; + total += ms; + + // Set a timer to be invoked when the next event is + // ready to play. + + OPL_SetCallback(ms, TrackTimerCallback, track); +} + +// Initialize a channel. + +static void InitChannel(opl_track_data_t *track, opl_channel_data_t *channel) +{ + // TODO: Work out sensible defaults? + + channel->instrument = &main_instrs[0]; + channel->volume = 127; + channel->bend = 0; +} + +// Start a MIDI track playing: + +static void StartTrack(const midi_file_t *file, unsigned int track_num) +{ + opl_track_data_t *track; + unsigned int i; + + track = &tracks[track_num]; + track->iter = MIDI_IterateTrack(file, track_num); + track->ticks_per_beat = MIDI_GetFileTimeDivision(file); + + // Default is 120 bpm. + // TODO: this is wrong + + track->ms_per_beat = 500; + + for (i=0; ichannels[i]); + } + + // Schedule the first event. + + ScheduleTrack(track); +} + +// Start playing a mid + +static void I_OPL_PlaySong(const void *handle, int looping) +{ + const midi_file_t *file; + unsigned int i; + + if (!music_initialized || handle == NULL) + { + return; + } + + file = (midi_file_t*)handle; + + // Allocate track data. + + tracks = (opl_track_data_t*)malloc(MIDI_NumTracks(file) * sizeof(opl_track_data_t)); + + num_tracks = MIDI_NumTracks(file); + running_tracks = num_tracks; + song_looping = looping; + + for (i=0; i 4 && !memcmp(mem, "MThd", 4); +} + +// now only takes files in MIDI format +static const void *I_OPL_RegisterSong(const void *data, unsigned len) +{ + midi_file_t *result; + midimem_t mf; + + if (!music_initialized) + { + return NULL; + } + mf.len = len; + mf.pos = 0; + mf.data = (byte*)data; + + // NSM: if a file has a miniscule timecode we have to not load it. + // if it's 0, we'll hang in scheduling and never finish. if it's + // very small but close to 0, there's probably something wrong with it + + // this check of course isn't very accurate, but to actually get the + // time numbers we have to traverse the tracks and everything + if (mf.len < 100) + { + lprintf (LO_WARN, "I_OPL_RegisterSong: Very short MIDI (%li bytes)\n", mf.len); + return NULL; + } + + result = MIDI_LoadFileSpecial (&mf); + + if (result == NULL) + { + lprintf (LO_WARN, "I_OPL_RegisterSong: Failed to load MID.\n"); + } + + + return result; +} + + +static void I_OPL_ShutdownMusic(void) +{ + if (music_initialized) + { + // Stop currently-playing track, if there is one: + + I_OPL_StopSong(); + + OPL_Shutdown(); + + // Release GENMIDI lump + + W_UnlockLumpName("GENMIDI"); + + music_initialized = false; + } +} + +// Initialize music subsystem + +int I_OPL_InitMusic(int samplerate) +{ + + if (!OPL_Init(samplerate)) + { + //printf("Dude. The Adlib isn't responding.\n"); + return 0; + } + + // Load instruments from GENMIDI lump: + + if (!LoadInstrumentTable()) + { + OPL_Shutdown(); + return false; + } + + InitVoices(); + + tracks = NULL; + num_tracks = 0; + music_initialized = true; + + return 1; +} + +const char *I_OPL_SynthName (void) +{ + return "opl2 synth player"; +} + +void I_OPL_RenderSamples (void *dest, unsigned nsamp) +{ + OPL_Render_Samples (dest, nsamp); +} + +const music_player_t opl_synth_player = +{ + I_OPL_SynthName, + I_OPL_InitMusic, + I_OPL_ShutdownMusic, + I_OPL_SetMusicVolume, + I_OPL_PauseSong, + I_OPL_ResumeSong, + I_OPL_RegisterSong, + I_OPL_UnRegisterSong, + I_OPL_PlaySong, + I_OPL_StopSong, + I_OPL_RenderSamples +}; + + + + diff --git a/src/MUSIC/oplplayer.h b/src/MUSIC/oplplayer.h new file mode 100644 index 0000000..697e271 --- /dev/null +++ b/src/MUSIC/oplplayer.h @@ -0,0 +1,34 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// System interface for music. +// +//----------------------------------------------------------------------------- + + +#ifndef OPLPLAYER_H +#define OPLPLAYER_H + +extern const music_player_t opl_synth_player; + + +#endif diff --git a/src/MUSIC/portmidiplayer.c b/src/MUSIC/portmidiplayer.c new file mode 100644 index 0000000..e5a9c29 --- /dev/null +++ b/src/MUSIC/portmidiplayer.c @@ -0,0 +1,685 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * + * Copyright (C) 2011 by + * Nicholai Main + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +// TODO: some duplicated code with this and the fluidplayer should be +// split off or something + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "musicplayer.h" + +#ifndef HAVE_LIBPORTMIDI +#include + +static const char *pm_name (void) +{ + return "portmidi midi player (DISABLED)"; +} + + +static int pm_init (int samplerate) +{ + return 0; +} + +const music_player_t pm_player = +{ + pm_name, + pm_init, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +#else // HAVE_LIBPORTMIDI + +#include +#include +#include +#include +#include +#include "lprintf.h" +#include "midifile.h" +#include "i_sound.h" // for snd_mididev +#include + +static midi_event_t **events; +static int eventpos; +static midi_file_t *midifile; + +static int pm_playing; +static int pm_paused; +static int pm_looping; +static int pm_volume = -1; +static double spmc; +static double pm_delta; + +static unsigned long trackstart; + +static PortMidiStream *pm_stream; + +#define SYSEX_BUFF_SIZE 1024 +static byte sysexbuff[SYSEX_BUFF_SIZE]; +static int sysexbufflen; + +// latency: we're generally writing timestamps slightly in the past (from when the last time +// render was called to this time. portmidi latency instruction must be larger than that window +// so the messages appear in the future. ~46-47ms is the nominal length if i_sound.c gets its way +#define DRIVER_LATENCY 80 // ms +// driver event buffer needs to be big enough to hold however many events occur in latency time +#define DRIVER_BUFFER 1024 // events + +static const char *pm_name (void) +{ + return "portmidi midi player"; +} + +#ifdef _MSC_VER +#define WIN32_LEAN_AND_MEAN +#include +#include +#endif + +#define DEFAULT_VOLUME 100 +static int channel_volume[16]; +static float volume_scale; + +static dboolean use_reset_delay; +static byte *sysex_reset; +static byte gs_reset[] = {0xf0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7f, 0x00, 0x41, 0xf7}; +static byte gm_system_on[] = {0xf0, 0x7e, 0x7f, 0x09, 0x01, 0xf7}; +static byte gm2_system_on[] = {0xf0, 0x7e, 0x7f, 0x09, 0x03, 0xf7}; +static byte xg_system_on[] = {0xf0, 0x43, 0x10, 0x4c, 0x00, 0x00, 0x7e, 0x00, 0xf7}; +static PmEvent event_notes_off[16]; +static PmEvent event_sound_off[16]; +static PmEvent event_reset[16 * 6]; +static PmEvent event_pbs[16 * 6]; +static PmEvent event_reverb[16]; +static PmEvent event_chorus[16]; + +static void reset_device (void) +{ + Pm_Write(pm_stream, event_notes_off, 16); + Pm_Write(pm_stream, event_sound_off, 16); + + if (sysex_reset == NULL) + Pm_Write(pm_stream, event_reset, 16 * 6); + else + Pm_WriteSysEx(pm_stream, 0, sysex_reset); + + Pm_Write(pm_stream, event_pbs, 16 * 6); + + if (mus_portmidi_reverb_level > -1 || sysex_reset == NULL) + Pm_Write(pm_stream, event_reverb, 16); + + if (mus_portmidi_chorus_level > -1 || sysex_reset == NULL) + Pm_Write(pm_stream, event_chorus, 16); + + use_reset_delay = mus_portmidi_reset_delay > 0; +} + +static void init_reset_buffer (void) +{ + int i; + PmEvent *reset = event_reset; + PmEvent *pbs = event_pbs; + int reverb = mus_portmidi_reverb_level; + int chorus = mus_portmidi_chorus_level; + + for (i = 0; i < 16; ++i) + { + event_notes_off[i].message = Pm_Message(0xB0 | i, 0x7B, 0x00); + event_sound_off[i].message = Pm_Message(0xB0 | i, 0x78, 0x00); + + reset[0].message = Pm_Message(0xB0 | i, 0x79, 0x00); // reset all controllers + reset[1].message = Pm_Message(0xB0 | i, 0x07, 0x64); // channel volume + reset[2].message = Pm_Message(0xB0 | i, 0x0A, 0x40); // pan + reset[3].message = Pm_Message(0xB0 | i, 0x00, 0x00); // bank select msb + reset[4].message = Pm_Message(0xB0 | i, 0x20, 0x00); // bank select lsb + reset[5].message = Pm_Message(0xC0 | i, 0x00, 0x00); // program change + reset += 6; + + pbs[0].message = Pm_Message(0xB0 | i, 0x64, 0x00); // pitch bend sens RPN LSB + pbs[1].message = Pm_Message(0xB0 | i, 0x65, 0x00); // pitch bend sens RPN MSB + pbs[2].message = Pm_Message(0xB0 | i, 0x06, 0x02); // data entry MSB + pbs[3].message = Pm_Message(0xB0 | i, 0x26, 0x00); // data entry LSB + pbs[4].message = Pm_Message(0xB0 | i, 0x64, 0x7F); // null RPN LSB + pbs[5].message = Pm_Message(0xB0 | i, 0x65, 0x7F); // null RPN MSB + pbs += 6; + } + + if (!strcasecmp(mus_portmidi_reset_type, "gs")) + sysex_reset = gs_reset; + else if (!strcasecmp(mus_portmidi_reset_type, "gm")) + sysex_reset = gm_system_on; + else if (!strcasecmp(mus_portmidi_reset_type, "gm2")) + sysex_reset = gm2_system_on; + else if (!strcasecmp(mus_portmidi_reset_type, "xg")) + sysex_reset = xg_system_on; + else + sysex_reset = NULL; + + // if no reverb specified and no SysEx reset selected, then use GM default + if (reverb == -1 && sysex_reset == NULL) + reverb = 40; + + if (reverb > -1) + { + for (i = 0; i < 16; ++i) + event_reverb[i].message = Pm_Message(0xB0 | i, 0x5B, reverb); + } + + // if no chorus specified and no SysEx reset selected, then use GM default + if (chorus == -1 && sysex_reset == NULL) + chorus = 0; + + if (chorus > -1) + { + for (i = 0; i < 16; ++i) + event_chorus[i].message = Pm_Message(0xB0 | i, 0x5D, chorus); + } +} + +static int pm_init (int samplerate) +{ + PmDeviceID outputdevice; + const PmDeviceInfo *oinfo; + int i; + char devname[64]; + + TESTDLLLOAD("portmidi.dll", TRUE) + + if (Pm_Initialize () != pmNoError) + { + lprintf (LO_WARN, "portmidiplayer: Pm_Initialize () failed\n"); + return 0; + } + + outputdevice = Pm_GetDefaultOutputDeviceID (); + + if (outputdevice == pmNoDevice) + { + lprintf (LO_WARN, "portmidiplayer: No output devices available\n"); + Pm_Terminate (); + return 0; + } + + // look for a device that matches the user preference + + lprintf (LO_INFO, "portmidiplayer device list:\n"); + for (i = 0; i < Pm_CountDevices (); i++) + { + oinfo = Pm_GetDeviceInfo (i); + if (!oinfo || !oinfo->output) + continue; + doom_snprintf (devname, 64, "%s:%s", oinfo->interf, oinfo->name); + if (strlen (snd_mididev) && strstr (devname, snd_mididev)) + { + outputdevice = i; + lprintf (LO_INFO, ">>%s\n", devname); + } + else + { + lprintf (LO_INFO, " %s\n", devname); + } + } + + oinfo = Pm_GetDeviceInfo (outputdevice); + + lprintf (LO_INFO, "portmidiplayer: Opening device %s:%s for output\n", oinfo->interf, oinfo->name); + + if (Pm_OpenOutput(&pm_stream, outputdevice, NULL, DRIVER_BUFFER, NULL, NULL, DRIVER_LATENCY) != pmNoError) + { + lprintf (LO_WARN, "portmidiplayer: Pm_OpenOutput () failed\n"); + Pm_Terminate (); + return 0; + } + + init_reset_buffer(); + reset_device(); + + for (int i = 0; i < 16; i++) + channel_volume[i] = DEFAULT_VOLUME; + + return 1; +} + +static void pm_stop (void); + +static void pm_shutdown (void) +{ + if (pm_stream) + { + // stop all sound, in case of hanging notes + if (pm_playing) + pm_stop(); + + /* ugly deadlock in portmidi win32 implementation: + + main thread gets stuck in Pm_Close + midi thread (started by windows) gets stuck in winmm_streamout_callback + + winapi ref says: + "Applications should not call any multimedia functions from inside the callback function, + as doing so can cause a deadlock. Other system functions can safely be called from the callback." + + winmm_streamout_callback calls midiOutUnprepareHeader. oops? + + + since timestamps are slightly in the future, it's very possible to have some messages still in + the windows midi queue when Pm_Close is called. this is normally no problem, but if one so happens + to dequeue and call winmm_streamout_callback at the exact right moment... + + fix: at this point, we've stopped generating midi messages. sleep for more than DRIVER_LATENCY to ensure + all messages are flushed. + + not a fix: calling Pm_Abort(); then midiStreamStop deadlocks instead of midiStreamClose. + */ + Pt_Sleep (DRIVER_LATENCY * 2); + + Pm_Close (pm_stream); + Pm_Terminate (); + pm_stream = NULL; + } +} + +static const void *pm_registersong (const void *data, unsigned len) +{ + midimem_t mf; + + mf.len = len; + mf.pos = 0; + mf.data = data; + + midifile = MIDI_LoadFile (&mf); + + if (!midifile) + { + lprintf (LO_WARN, "pm_registersong: Failed to load MIDI.\n"); + return NULL; + } + + events = MIDI_GenerateFlatList (midifile); + if (!events) + { + MIDI_FreeFile (midifile); + return NULL; + } + eventpos = 0; + + spmc = MIDI_spmc (midifile, NULL, 1000); + + // handle not used + return data; +} + +static void writeevent (unsigned long when, int eve, int channel, int v1, int v2) +{ + PmMessage m; + + m = Pm_Message (eve | channel, v1, v2); + Pm_WriteShort (pm_stream, when, m); +} + +static void write_volume (unsigned long when, int channel, int volume) +{ + int vol = volume * volume_scale + 0.5f; + writeevent (when, MIDI_EVENT_CONTROLLER, channel, MIDI_CONTROLLER_MAIN_VOLUME, vol); + channel_volume[channel] = volume; +} + +static void update_volume (void) +{ + for (int i = 0; i < 16; i++) + write_volume (0, i, channel_volume[i]); +} + +static void reset_volume (void) +{ + for (int i = 0; i < 16; i++) + write_volume (0, i, DEFAULT_VOLUME); +} + +static void pm_setvolume (int v) +{ + if (pm_volume == v) + return; + + pm_volume = v; + volume_scale = sqrtf((float)pm_volume / 15); + update_volume(); +} + +static void pm_unregistersong (const void *handle) +{ + if (events) + { + MIDI_DestroyFlatList (events); + events = NULL; + } + if (midifile) + { + MIDI_FreeFile (midifile); + midifile = NULL; + } +} + +static void pm_pause (void) +{ + pm_paused = 1; + Pm_Write(pm_stream, event_notes_off, 16); + Pm_Write(pm_stream, event_sound_off, 16); +} + +static void pm_resume (void) +{ + pm_paused = 0; + trackstart = Pt_Time (); +} + +static void pm_play (const void *handle, int looping) +{ + eventpos = 0; + pm_looping = looping; + pm_playing = 1; + pm_delta = 0.0; + if (pm_volume != -1) // set pm_volume first, see pm_setvolume() + reset_volume(); + trackstart = Pt_Time (); +} + +static dboolean is_sysex_reset (byte *msg, int len) +{ + if (len < 6) + return false; + + switch (msg[1]) + { + case 0x41: // roland + switch (msg[3]) + { + case 0x42: // gs + switch (msg[4]) + { + case 0x12: // dt1 + if (len == 11 && + msg[5] == 0x00 && // address msb + msg[6] == 0x00 && // address + msg[7] == 0x7F && // address lsb + ((msg[8] == 0x00 && // data (mode-1) + msg[9] == 0x01) || // checksum (mode-1) + (msg[8] == 0x01 && // data (mode-2) + msg[9] == 0x00))) // checksum (mode-2) + { + // sc-88 system mode set + // F0 41 42 12 00 00 7F 00 01 F7 (mode-1) + // F0 41 42 12 00 00 7F 01 00 F7 (mode-2) + return true; + } + else if (len == 11 && + msg[5] == 0x40 && // address msb + msg[6] == 0x00 && // address + msg[7] == 0x7F && // address lsb + msg[8] == 0x00 && // data (gs reset) + msg[9] == 0x41) // checksum + { + // gs reset + // F0 41 42 12 40 00 7F 00 41 F7 + return true; + } + break; + } + break; + } + break; + + case 0x43: // yamaha + switch (msg[3]) + { + case 0x2B: // tg300 + if (len == 10 && + msg[4] == 0x00 && // start address b20 - b14 + msg[5] == 0x00 && // start address b13 - b7 + msg[6] == 0x7F && // start address b6 - b0 + msg[7] == 0x00 && // data + msg[8] == 0x01) // checksum + { + // tg300 all parameter reset + // F0 43 2B 00 00 7F 00 01 F7 + return true; + } + break; + + case 0x4C: // xg + if (len == 9 && + msg[4] == 0x00 && // address high + msg[5] == 0x00 && // address mid + (msg[6] == 0x7E || // address low (xg system on) + msg[6] == 0x7F) && // address low (xg all parameter reset) + msg[7] == 0x00) // data + { + // xg system on, xg all parameter reset + // F0 43 4C 00 00 7E 00 F7 + // F0 43 4C 00 00 7F 00 F7 + return true; + } + break; + } + break; + + case 0x7E: // universal non-real time + switch (msg[3]) + { + case 0x09: // general midi + if (len == 6 && + (msg[4] == 0x01 || // gm system on + msg[4] == 0x02 || // gm system off + msg[4] == 0x03)) // gm2 system on + { + // gm system on/off, gm2 system on + // F0 7E 09 01 F7 + // F0 7E 09 02 F7 + // F0 7E 09 03 F7 + return true; + } + break; + } + break; + } + + return false; +} + +static void writesysex (unsigned long when, int etype, byte *data, int len) +{ + // sysex messages in midi files (smf 1.0 pages 6-7): + // complete: (F0 ... F7) + // multi-packet: (F0 ...) + (F7 ...) + ... + (F7 ... F7) + // escape sequence: (F7 ...) + + if (len + sysexbufflen > SYSEX_BUFF_SIZE - 1) + { + // ignore messages that are too long + sysexbufflen = 0; + return; + } + + if (etype == MIDI_EVENT_SYSEX_SPLIT && sysexbufflen == 0) + { + // ignore escape sequence + return; + } + + if (etype == MIDI_EVENT_SYSEX) + { + // start a new message (discards any previous incomplete message) + sysexbuff[0] = MIDI_EVENT_SYSEX; + sysexbufflen = 1; + } + + memcpy (sysexbuff + sysexbufflen, data, len); + sysexbufflen += len; + + // process message if it's complete, otherwise do nothing yet + if (sysexbuff[sysexbufflen - 1] == MIDI_EVENT_SYSEX_SPLIT) + { + Pm_WriteSysEx (pm_stream, when, sysexbuff); + + if (is_sysex_reset(sysexbuff, sysexbufflen)) + reset_volume(); + + sysexbufflen = 0; + } +} + +static void pm_stop (void) +{ + pm_playing = 0; + + // songs can be stopped at any time, so reset everything + reset_device(); + + // abort any partial sysex + sysexbufflen = 0; +} + +static void pm_render (void *vdest, unsigned bufflen) +{ + // wherever you see samples in here, think milliseconds + unsigned long when = trackstart; + unsigned long newtime = Pt_Time (); + unsigned int samples; + + memset (vdest, 0, bufflen * 4); + + if (!pm_playing || pm_paused) + return; + + while (1) + { + midi_event_t *currevent = events[eventpos]; + + // how many samples away event is + double eventdelta = currevent->delta_time * spmc; + + // delay after reset, for real devices only (e.g. roland sc-55) + if (use_reset_delay) + eventdelta += mus_portmidi_reset_delay; + + // how many we will render (rounding down); include delta offset + samples = eventdelta + pm_delta; + + if (when + samples > newtime) + { + // overshoot; render some samples without processing an event + pm_delta -= (newtime - when); // save offset + trackstart = newtime; + return; + } + + use_reset_delay = false; + pm_delta += eventdelta - samples; + when += samples; + + switch (currevent->event_type) + { + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + if (!mus_portmidi_filter_sysex) + writesysex (when, currevent->event_type, currevent->data.sysex.data, currevent->data.sysex.length); + break; + case MIDI_EVENT_META: + switch (currevent->data.meta.type) + { + case MIDI_META_SET_TEMPO: + spmc = MIDI_spmc (midifile, currevent, 1000); + break; + case MIDI_META_END_OF_TRACK: + if (pm_looping) + { + eventpos = 0; + // prevent hanging notes (doom2.wad MAP14, MAP22) + for (int i = 0; i < 16; i++) + { + writeevent (when, 0xB0, i, 0x7B, 0x00); // all notes off + writeevent (when, 0xB0, i, 0x79, 0x00); // reset all controllers + } + continue; + } + pm_stop(); + return; + } + break; // not interested in most metas + case MIDI_EVENT_CONTROLLER: + if (currevent->data.channel.param1 == MIDI_CONTROLLER_MAIN_VOLUME) + { + write_volume (when, currevent->data.channel.channel, currevent->data.channel.param2); + break; + } + else if (currevent->data.channel.param1 == 0x79) + { + // ms gs synth resets volume if "reset all controllers" value isn't zero + writeevent (when, 0xB0, currevent->data.channel.channel, 0x79, 0x00); + break; + } + // fall through + default: + writeevent (when, currevent->event_type, currevent->data.channel.channel, currevent->data.channel.param1, currevent->data.channel.param2); + break; + } + + eventpos++; + } +} + +const music_player_t pm_player = +{ + pm_name, + pm_init, + pm_shutdown, + pm_setvolume, + pm_pause, + pm_resume, + pm_registersong, + pm_unregistersong, + pm_play, + pm_stop, + pm_render +}; + +#endif // HAVE_LIBPORTMIDI diff --git a/src/MUSIC/portmidiplayer.h b/src/MUSIC/portmidiplayer.h new file mode 100644 index 0000000..4f1b5c8 --- /dev/null +++ b/src/MUSIC/portmidiplayer.h @@ -0,0 +1,47 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * + * Copyright (C) 2011 by + * Nicholai Main + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifndef PORTMIDIPLAYER_H +#define PORTMIDIPLAYER_H + + + + +extern const music_player_t pm_player; + + + + + + + + + +#endif // PORTMIDI_H diff --git a/src/MUSIC/vorbisplayer.c b/src/MUSIC/vorbisplayer.c new file mode 100644 index 0000000..541e7bb --- /dev/null +++ b/src/MUSIC/vorbisplayer.c @@ -0,0 +1,468 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * + * Copyright (C) 2011 by + * Nicholai Main + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "musicplayer.h" + +#ifndef HAVE_LIBVORBISFILE +#include + +static const char *vorb_name (void) +{ + return "vorbis player (DISABLED)"; +} + + +static int vorb_init (int samplerate) +{ + return 0; +} + +const music_player_t vorb_player = +{ + vorb_name, + vorb_init, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +#else // HAVE_LIBVORBISFILE + + +#include +#include +#include "lprintf.h" + +#include +#include + +#include "i_sound.h" + +// uncomment to allow (experiemntal) support for +// zdoom-style audio loops +#define ZDOOM_AUDIO_LOOP + +static int vorb_looping = 0; +static int vorb_volume = 0; // 0-15 +static int vorb_samplerate_target = 0; +static int vorb_samplerate_in = 0; +static int vorb_paused = 0; +static int vorb_playing = 0; + +#ifdef ZDOOM_AUDIO_LOOP +static unsigned vorb_loop_from; +static unsigned vorb_loop_to; +static unsigned vorb_total_pos; +#endif // ZDOOM_AUDIO_LOOP + +static const char *vorb_data; +static size_t vorb_len; +static size_t vorb_pos; + +OggVorbis_File vf; + +// io callbacks + +static size_t vread (void *dst, size_t s, size_t n, void *src) +{ + size_t size = s * n; + + if (vorb_pos + size >= vorb_len) + size = vorb_len - vorb_pos; + + memcpy (dst, vorb_data + vorb_pos, size); + vorb_pos += size; + return size; +} + +static int vseek (void *src, ogg_int64_t offset, int whence) +{ + size_t desired_pos; + + switch (whence) + { + case SEEK_SET: + desired_pos = (size_t) offset; + break; + case SEEK_CUR: + desired_pos = vorb_pos + (size_t) offset; + break; + case SEEK_END: + default: + desired_pos = vorb_len + (size_t) offset; + break; + } + if (desired_pos > vorb_len) // placing exactly at the end is allowed) + return -1; + vorb_pos = desired_pos; + return 0; +} + +static long vtell (void *src) +{ + // correct to vorbisfile spec, this is a long, not 64 bit + return (long) vorb_pos; +} + + +ov_callbacks vcallback = +{ + vread, + vseek, + NULL, + vtell +}; + + +static const char *vorb_name (void) +{ + return "vorbis player"; +} + +// http://zdoom.org/wiki/Audio_loop +// it's hard to accurately implement a grammar with "etc" in the spec, +// so weird edge cases are likely not the same +#ifdef ZDOOM_AUDIO_LOOP +static unsigned parsetag (const char *str, int samplerate) +{ + int ret = 0; + int seendot = 0; // number of dots seen so far + int mult = 1; // place value of next digit out + int seencolon = 0; // number of colons seen so far + int digincol = 0; // number of digits in current group + // (needed to track switch between colon) + + const char *pos = str + strlen (str) - 1; + + for (; pos >= str; pos--) + { + if (*pos >= '0' && *pos <= '9') + { + ret += (*pos - '0') * mult; + mult *= 10; + digincol++; + } + else if (*pos == '.') + { + if (seencolon || seendot) + return 0; + seendot = 1; + // convert decimal to samplerate and move on + ret *= samplerate; + ret /= mult; + mult = samplerate; + digincol = 0; + } + else if (*pos == ':') + { + if (seencolon == 2) // no mention of anything past hours in spec + return 0; + seencolon++; + mult *= 6; + + // the spec is kind of vague and says lots of things can be left out, + // so constructs like mmm:ss and hh::ss are allowed + while (digincol > 1) + { + digincol--; + mult /= 10; + } + while (digincol < 1) + { + digincol++; + mult *= 10; + } + digincol = 0; + } + else + return 0; + } + if (seencolon && !seendot) + { // HH:MM:SS, but we never converted to samples + return ret * samplerate; + } + // either flat pcm or :. in which case everything was converted already + return ret; +} +#endif // ZDOOM_AUDIO_LOOP + +#ifdef _MSC_VER +#define WIN32_LEAN_AND_MEAN +#include +#include +#endif + +static int vorb_init (int samplerate) +{ + TESTDLLLOAD ("libogg-0.dll", FALSE) + TESTDLLLOAD ("libvorbis-0.dll", FALSE) + TESTDLLLOAD ("libvorbisfile-3.dll", TRUE) + vorb_samplerate_target = samplerate; + return 1; +} + +static void vorb_shutdown (void) +{ + // nothing to do + + +} + +static const void *vorb_registersong (const void *data, unsigned len) +{ + int i; + vorbis_info *vinfo; + #ifdef ZDOOM_AUDIO_LOOP + vorbis_comment *vcom; + #endif // ZDOOM_AUDIO_LOOP + + vorb_data = (const char*)data; + vorb_len = len; + vorb_pos = 0; + + i = ov_test_callbacks ((void *) data, &vf, NULL, 0, vcallback); + + if (i != 0) + { + lprintf (LO_WARN, "vorb_registersong: failed\n"); + return NULL; + } + i = ov_test_open (&vf); + + if (i != 0) + { + lprintf (LO_WARN, "vorb_registersong: failed\n"); + ov_clear (&vf); + return NULL; + } + + vinfo = ov_info (&vf, -1); + vorb_samplerate_in = vinfo->rate; + + #ifdef ZDOOM_AUDIO_LOOP + // parse LOOP_START LOOP_END tags + vorb_loop_from = 0; + vorb_loop_to = 0; + + vcom = ov_comment (&vf, -1); + for (i = 0; i < vcom->comments; i++) + { + if (strncmp ("LOOP_START=", vcom->user_comments[i], 11) == 0) + vorb_loop_to = parsetag (vcom->user_comments[i] + 11, vorb_samplerate_in); + else if (strncmp ("LOOP_END=", vcom->user_comments[i], 9) == 0) + vorb_loop_from = parsetag (vcom->user_comments[i] + 9, vorb_samplerate_in); + } + if (vorb_loop_from == 0) + vorb_loop_from = 0xffffffff; + else if (vorb_loop_to >= vorb_loop_from) + vorb_loop_to = 0; + #endif // ZDOOM_AUDIO_LOOP + + // handle not used + return data; +} + +static void vorb_setvolume (int v) +{ + vorb_volume = v; +} + +static void vorb_pause (void) +{ + vorb_paused = 1; +} + +static void vorb_resume (void) +{ + vorb_paused = 0; +} + +static void vorb_unregistersong (const void *handle) +{ + vorb_data = NULL; + ov_clear (&vf); + vorb_playing = 0; +} + +static void vorb_play (const void *handle, int looping) +{ + ov_raw_seek_lap (&vf, 0); + + + vorb_playing = 1; + vorb_looping = looping; + #ifdef ZDOOM_AUDIO_LOOP + vorb_total_pos = 0; + #endif // ZDOOM_AUDIO_LOOP +} + +static void vorb_stop (void) +{ + vorb_playing = 0; +} + +static void vorb_render_ex (void *dest, unsigned nsamp) +{ + // no workie on files that dynamically change sampling rate + + short *sout = (short *) dest; + + int bitstreamnum; // not used + + float **pcmdata; + + float multiplier; + + int localerrors = 0; + + //int nchannel; + //int freq; + int numread; + + int i; + + // this call needs to be moved if support for changed number + // of channels in middle of file is wanted + vorbis_info *vinfo = ov_info (&vf, -1); + + + if (!vorb_playing || vorb_paused) + { + memset (dest, 0, nsamp * 4); + return; + } + + while (nsamp > 0) + { + #ifdef ZDOOM_AUDIO_LOOP + // don't use custom loop end point when not in looping mode + if (vorb_looping && vorb_total_pos + nsamp > vorb_loop_from) + numread = ov_read_float (&vf, &pcmdata, vorb_loop_from - vorb_total_pos, &bitstreamnum); + else + #endif // ZDOOM_AUDIO_LOOP + numread = ov_read_float (&vf, &pcmdata, nsamp, &bitstreamnum); + if (numread == OV_HOLE) + { // recoverable error, but discontinue if we get too many + localerrors++; + if (localerrors == 10) + { + lprintf (LO_WARN, "vorb_render: many errors. aborting\n"); + vorb_playing = 0; + memset (sout, 0, nsamp * 4); + return; + } + continue; + } + else if (numread == 0) + { // EOF + if (vorb_looping) + { + #ifdef ZDOOM_AUDIO_LOOP + ov_pcm_seek_lap (&vf, vorb_loop_to); + vorb_total_pos = vorb_loop_to; + #else // ZDOOM_AUDIO_LOOP + ov_raw_seek_lap (&vf, 0); + #endif // ZDOOM_AUDIO_LOOP + continue; + } + else + { + vorb_playing = 0; + memset (sout, 0, nsamp * 4); + return; + } + } + else if (numread < 0) + { // unrecoverable errror + lprintf (LO_WARN, "vorb_render: unrecoverable error\n"); + vorb_playing = 0; + memset (sout, 0, nsamp * 4); + return; + } + + multiplier = 16384.0f / 15.0f * vorb_volume; + // volume and downmix + if (vinfo->channels == 2) + { + for (i = 0; i < numread; i++, sout += 2) + { // data is preclipped? + sout[0] = (short) (pcmdata[0][i] * multiplier); + sout[1] = (short) (pcmdata[1][i] * multiplier); + } + } + else // mono + { + for (i = 0; i < numread; i++, sout += 2) + { + sout[0] = sout [1] = (short) (pcmdata[0][i] * multiplier); + } + } + nsamp -= numread; + #ifdef ZDOOM_AUDIO_LOOP + vorb_total_pos += numread; + #endif // ZDOOM_AUDIO_LOOP + } + +} + +static void vorb_render (void *dest, unsigned nsamp) +{ + I_ResampleStream (dest, nsamp, vorb_render_ex, vorb_samplerate_in, vorb_samplerate_target); +} + + +const music_player_t vorb_player = +{ + vorb_name, + vorb_init, + vorb_shutdown, + vorb_setvolume, + vorb_pause, + vorb_resume, + vorb_registersong, + vorb_unregistersong, + vorb_play, + vorb_stop, + vorb_render +}; + +#endif // HAVE_LIBVORBISFILE diff --git a/src/MUSIC/vorbisplayer.h b/src/MUSIC/vorbisplayer.h new file mode 100644 index 0000000..64e79b1 --- /dev/null +++ b/src/MUSIC/vorbisplayer.h @@ -0,0 +1,47 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * + * Copyright (C) 2011 by + * Nicholai Main + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + + +#ifndef VORBISPLAYER_H +#define VORBISPLAYER_H + + + +extern const music_player_t vorb_player; + + + + + + + + + +#endif // VORBISPLAYER_H diff --git a/src/PCSOUND/pcsound.c b/src/PCSOUND/pcsound.c new file mode 100644 index 0000000..4e1d806 --- /dev/null +++ b/src/PCSOUND/pcsound.c @@ -0,0 +1,127 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2007 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// PC speaker interface. +// +//----------------------------------------------------------------------------- + +#include +#include +#include + +#include "config.h" +#include "pcsound.h" + +//e6y +#include "doomtype.h" +#include "lprintf.h" + +#ifdef USE_WIN32_PCSOUND_DRIVER +extern pcsound_driver_t pcsound_win32_driver; +#endif + +#ifdef HAVE_LINUX_KD_H +extern pcsound_driver_t pcsound_linux_driver; +#endif + +extern pcsound_driver_t pcsound_sdl_driver; + +static pcsound_driver_t *drivers[] = +{ +#ifdef HAVE_LINUX_KD_H + &pcsound_linux_driver, +#endif +#ifdef USE_WIN32_PCSOUND_DRIVER + &pcsound_win32_driver, +#endif + &pcsound_sdl_driver, + NULL, +}; + +static pcsound_driver_t *pcsound_driver = NULL; + +int PCSound_Init(pcsound_callback_func callback_func) +{ + char *driver_name; + int i; + + if (pcsound_driver != NULL) + { + return 1; + } + + // Check if the environment variable is set + + driver_name = getenv("PCSOUND_DRIVER"); + + if (driver_name != NULL) + { + for (i=0; drivers[i] != NULL; ++i) + { + if (!strcasecmp(drivers[i]->name, driver_name)) + { + // Found the driver! + + if (drivers[i]->init_func(callback_func)) + { + pcsound_driver = drivers[i]; + } + else + { + lprintf(LO_WARN, "Failed to initialise PC sound driver: %s\n", + drivers[i]->name); + break; + } + } + } + } + else + { + // Try all drivers until we find a working one + + for (i=0; drivers[i] != NULL; ++i) + { + if (drivers[i]->init_func(callback_func)) + { + pcsound_driver = drivers[i]; + break; + } + } + } + + if (pcsound_driver != NULL) + { + lprintf(LO_INFO, "Using PC sound driver: %s\n", pcsound_driver->name); + return 1; + } + else + { + lprintf(LO_WARN, "Failed to find a working PC sound driver.\n"); + return 0; + } +} + +void PCSound_Shutdown(void) +{ + pcsound_driver->shutdown_func(); + pcsound_driver = NULL; +} + diff --git a/src/PCSOUND/pcsound.h b/src/PCSOUND/pcsound.h new file mode 100644 index 0000000..5b4d5d8 --- /dev/null +++ b/src/PCSOUND/pcsound.h @@ -0,0 +1,47 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2007 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// PC speaker interface. +// +//----------------------------------------------------------------------------- + +#ifndef PCSOUND_H +#define PCSOUND_H + +#define PCSOUND_8253_FREQUENCY 1193280 + +typedef struct pcsound_driver_s pcsound_driver_t; +typedef void (*pcsound_callback_func)(int *duration, int *frequency); +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; +}; + +int PCSound_Init(pcsound_callback_func callback_func); +void PCSound_Shutdown(void); + +#endif /* #ifndef PCSOUND_H */ + diff --git a/src/PCSOUND/pcsound_linux.c b/src/PCSOUND/pcsound_linux.c new file mode 100644 index 0000000..c939178 --- /dev/null +++ b/src/PCSOUND/pcsound_linux.c @@ -0,0 +1,129 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2007 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// PC speaker driver for Linux. +// +//----------------------------------------------------------------------------- + +#include "config.h" + +#ifdef HAVE_LINUX_KD_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SDL.h" +#include "SDL_thread.h" + +#include "pcsound.h" + +//e6y +#include "lprintf.h" + +#define CONSOLE_DEVICE "/dev/console" + +static int console_handle; +static pcsound_callback_func callback; +static int sound_thread_running = 0; +static SDL_Thread *sound_thread_handle; + +static int SoundThread(void *unused) +{ + int frequency; + int duration; + int cycles; + + while (sound_thread_running) + { + callback(&duration, &frequency); + + if (frequency != 0) + { + cycles = PCSOUND_8253_FREQUENCY / frequency; + } + else + { + cycles = 0; + } + + ioctl(console_handle, KIOCSOUND, cycles); + + usleep(duration * 1000); + } + + return 0; +} + +static int PCSound_Linux_Init(pcsound_callback_func callback_func) +{ + // Try to open the console + + console_handle = open(CONSOLE_DEVICE, O_WRONLY); + + if (console_handle == -1) + { + // Don't have permissions for the console device? + + lprintf(LO_WARN, "PCSound_Linux_Init: Failed to open '%s': %s\n", + CONSOLE_DEVICE, strerror(errno)); + return 0; + } + + if (ioctl(console_handle, KIOCSOUND, 0) < 0) + { + // KIOCSOUND not supported: non-PC linux? + + close(console_handle); + return 0; + } + + // Start a thread up to generate PC speaker output + + callback = callback_func; + sound_thread_running = 1; + + sound_thread_handle = SDL_CreateThread(SoundThread, "sound_thread_handle", NULL); + + return 1; +} + +static void PCSound_Linux_Shutdown(void) +{ + sound_thread_running = 0; + SDL_WaitThread(sound_thread_handle, NULL); + close(console_handle); +} + +pcsound_driver_t pcsound_linux_driver = +{ + "Linux", + PCSound_Linux_Init, + PCSound_Linux_Shutdown, +}; + +#endif /* #ifdef HAVE_LINUX_KD_H */ + diff --git a/src/PCSOUND/pcsound_sdl.c b/src/PCSOUND/pcsound_sdl.c new file mode 100644 index 0000000..2d353e4 --- /dev/null +++ b/src/PCSOUND/pcsound_sdl.c @@ -0,0 +1,180 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2007 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// PC speaker interface. +// +//----------------------------------------------------------------------------- + +// NSM: reworked to function without sdl_mixer (needs new i_sound.c) + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "SDL.h" + +#include "pcsound.h" + +//e6y +#include "lprintf.h" + +#include "i_sound.h" // snd_samplerate + +#define SQUARE_WAVE_AMP 0x2000 + +static pcsound_callback_func callback; + +// Output sound format + +static int mixing_freq; +//static Uint16 mixing_format; +//static int mixing_channels; + +// Currently playing sound +// current_remaining is the number of remaining samples that must be played +// before we invoke the callback to get the next frequency. + +static int current_remaining; +static int current_freq; + +static int phase_offset = 0; + +static int pcsound_inited = 0; + +// Mixer function that does the PC speaker emulation + +void PCSound_Mix_Callback(void *udata, Uint8 *stream, int len) +{ + Sint16 *leftptr; + Sint16 *rightptr; + Sint16 this_value; + int oldfreq; + int i; + int nsamples; + + // safe to call even if not active + if (!pcsound_inited) + return; + + + // Number of samples is quadrupled, because of 16-bit and stereo + + nsamples = len / 4; + + leftptr = (Sint16 *) stream; + rightptr = ((Sint16 *) stream) + 1; + + // Fill the output buffer + + for (i=0; i + +#include "pcsound.h" + +static SDL_Thread *sound_thread_handle; +static int sound_thread_running; +static pcsound_callback_func callback; + +static int SoundThread(void *unused) +{ + int frequency; + int duration; + + while (sound_thread_running) + { + callback(&duration, &frequency); + + if (frequency != 0) + { + Beep(frequency, duration); + } + else + { + Sleep(duration); + } + } + + return 0; +} + +static int PCSound_Win32_Init(pcsound_callback_func callback_func) +{ + OSVERSIONINFO osvi; + BOOL result; + + // Temporarily disabled - the Windows scheduler is strange and + // stupid. + + return 0; + + // Find the OS version + + osvi.dwOSVersionInfoSize = sizeof(osvi); + + result = GetVersionEx(&osvi); + + if (!result) + { + return 0; + } + + // Beep() ignores its arguments on win9x, so this driver will + // not work there. + + if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) + { + // TODO: Use _out() to write directly to the PC speaker on + // win9x: See PC/winsound.c in the Python standard library. + + return 0; + } + + // Start a thread to play sound. + + callback = callback_func; + sound_thread_running = 1; + + sound_thread_handle = SDL_CreateThread(SoundThread, "pcsound_thread_handle", NULL); + + return 1; +} + +static void PCSound_Win32_Shutdown(void) +{ + sound_thread_running = 0; + SDL_WaitThread(sound_thread_handle, NULL); +} + +pcsound_driver_t pcsound_win32_driver = +{ + "Windows", + PCSound_Win32_Init, + PCSound_Win32_Shutdown, +}; + +#endif /* #ifdef _WIN32 */ + diff --git a/src/POSIX/i_system.c b/src/POSIX/i_system.c new file mode 100644 index 0000000..46d23ef --- /dev/null +++ b/src/POSIX/i_system.c @@ -0,0 +1,136 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2006 by Colin Phipps, Florian Schulze + * + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Misc system stuff needed by Doom, implemented for POSIX systems. + * Timers and signals. + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "doomtype.h" +#include "m_fixed.h" +#include "i_system.h" +#include "doomdef.h" + +#ifdef __GNUG__ +#pragma implementation "i_system.h" +#endif +#include "i_system.h" + +void I_uSleep(unsigned long usecs) +{ +#ifdef HAVE_USLEEP + usleep(usecs); +#else + /* Fall back on select(2) */ + struct timeval tv = { usecs / 1000000, usecs % 1000000 }; + select(0,NULL,NULL,NULL,&tv); +#endif +} + +/* CPhipps - believe it or not, it is possible with consecutive calls to + * gettimeofday to receive times out of order, e.g you query the time twice and + * the second time is earlier than the first. Cheap'n'cheerful fix here. + * NOTE: only occurs with bad kernel drivers loaded, e.g. pc speaker drv + */ + +static unsigned long lasttimereply; +static unsigned long basetime; + +int I_GetTime_RealTime (void) +{ + struct timeval tv; + struct timezone tz; + unsigned long thistimereply; + + gettimeofday(&tv, &tz); + + thistimereply = (tv.tv_sec * TICRATE + (tv.tv_usec * TICRATE) / 1000000); + + /* Fix for time problem */ + if (!basetime) { + basetime = thistimereply; thistimereply = 0; + } else thistimereply -= basetime; + + if (thistimereply < lasttimereply) + thistimereply = lasttimereply; + + return (lasttimereply = thistimereply); +} + +/* + * I_GetRandomTimeSeed + * + * CPhipps - extracted from G_ReloadDefaults because it is O/S based + */ +unsigned long I_GetRandomTimeSeed(void) +{ + /* killough 3/26/98: shuffle random seed, use the clock */ + struct timeval tv; + struct timezone tz; + gettimeofday(&tv,&tz); + return (tv.tv_sec*1000ul + tv.tv_usec/1000ul); +} + +/* cphipps - I_GetVersionString + * Returns a version string in the given buffer + */ +const char* I_GetVersionString(char* buf, size_t sz) +{ + snprintf(buf,sz,"%s v%s (%s)",PACKAGE_NAME,PACKAGE_VERSION,PACKAGE_HOMEPAGE); + return buf; +} + +/* cphipps - I_SigString + * Returns a string describing a signal number + */ +const char* I_SigString(char* buf, size_t sz, int signum) +{ +#ifdef HAVE_STRSIGNAL + if (strsignal(signum) && strlen(strsignal(signum)) < sz) + strcpy(buf,strsignal(signum)); + else +#endif + sprintf(buf,"signal %d",signum); + return buf; +} diff --git a/src/SDL/SDL_windows.h b/src/SDL/SDL_windows.h new file mode 100644 index 0000000..0c99b03 --- /dev/null +++ b/src/SDL/SDL_windows.h @@ -0,0 +1,64 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2016 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* This is an include file for windows.h with the SDL build settings */ + +#ifndef _INCLUDED_WINDOWS_H +#define _INCLUDED_WINDOWS_H + +#if defined(__WIN32__) +#define WIN32_LEAN_AND_MEAN +#define STRICT +#ifndef UNICODE +#define UNICODE 1 +#endif +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x501 /* Need 0x410 for AlphaBlend() and 0x500 for EnumDisplayDevices(), 0x501 for raw input */ +#endif + +#include + +/* Routines to convert from UTF8 to native Windows text */ +#if UNICODE +#define WIN_StringToUTF8(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(WCHAR)) +#define WIN_UTF8ToString(S) (WCHAR *)SDL_iconv_string("UTF-16LE", "UTF-8", (char *)(S), SDL_strlen(S)+1) +#else +/* !!! FIXME: UTF8ToString() can just be a SDL_strdup() here. */ +#define WIN_StringToUTF8(S) SDL_iconv_string("UTF-8", "ASCII", (char *)(S), (SDL_strlen(S)+1)) +#define WIN_UTF8ToString(S) SDL_iconv_string("ASCII", "UTF-8", (char *)(S), SDL_strlen(S)+1) +#endif + +/* Sets an error message based on a given HRESULT */ +extern int WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr); + +/* Sets an error message based on GetLastError(). Always return -1. */ +extern int WIN_SetError(const char *prefix); + +/* Wrap up the oddities of CoInitialize() into a common function. */ +extern HRESULT WIN_CoInitialize(void); +extern void WIN_CoUninitialize(void); + +/* Returns SDL_TRUE if we're running on Windows Vista and newer */ +extern BOOL WIN_IsWindowsVistaOrGreater(); + +#endif /* _INCLUDED_WINDOWS_H */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/SDL/SDL_windows_main.c b/src/SDL/SDL_windows_main.c new file mode 100644 index 0000000..3c17d51 --- /dev/null +++ b/src/SDL/SDL_windows_main.c @@ -0,0 +1,109 @@ +/* + SDL_windows_main.c, placed in the public domain by Sam Lantinga 4/13/98 + + The WinMain function -- calls your program's main() function +*/ +#include "SDL_config.h" + +#ifdef __WIN32__ + +/* Include this so we define UNICODE properly */ +//#include "../../core/windows/SDL_windows.h" +#include "SDL/SDL_windows.h" +#include /* CommandLineToArgvW() */ + +/* Include the SDL main definition header */ +#include "SDL.h" +#include "SDL_main.h" + +#ifdef main +# undef main +#endif /* main */ + +#define WIN_WStringToUTF8(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(WCHAR)) + +/* Pop up an out of memory message, returns to Windows */ +static BOOL +OutOfMemory(void) +{ + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Out of memory - aborting", NULL); + return FALSE; +} + +#if defined(_MSC_VER) +/* The VC++ compiler needs main/wmain defined */ +# define console_ansi_main main +# if UNICODE +# define console_wmain wmain +# endif +#endif + +/* Gets the arguments with GetCommandLine, converts them to argc and argv + and calls SDL_main */ +static int +main_getcmdline(void) +{ + LPWSTR *argvw; + char **argv; + int i, argc, result; + + argvw = CommandLineToArgvW(GetCommandLineW(), &argc); + if (argvw == NULL) { + return OutOfMemory(); + } + + /* Parse it into argv and argc */ + argv = (char **)SDL_calloc(argc + 1, sizeof(*argv)); + if (!argv) { + return OutOfMemory(); + } + for (i = 0; i < argc; ++i) { + argv[i] = WIN_WStringToUTF8(argvw[i]); + if (!argv[i]) { + return OutOfMemory(); + } + } + argv[i] = NULL; + LocalFree(argvw); + + SDL_SetMainReady(); + + /* Run the application main() code */ + result = SDL_main(argc, argv); + + /* Free argv, to avoid memory leak */ + for (i = 0; i < argc; ++i) { + SDL_free(argv[i]); + } + SDL_free(argv); + + return result; +} + +/* This is where execution begins [console apps, ansi] */ +int +console_ansi_main(int argc, char *argv[]) +{ + return main_getcmdline(); +} + + +#if UNICODE +/* This is where execution begins [console apps, unicode] */ +int +console_wmain(int argc, wchar_t *wargv[], wchar_t *wenvp) +{ + return main_getcmdline(); +} +#endif + +/* This is where execution begins [windowed apps] */ +int WINAPI +WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) +{ + return main_getcmdline(); +} + +#endif /* __WIN32__ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/SDL/i_joy.c b/src/SDL/i_joy.c new file mode 100644 index 0000000..6ced87f --- /dev/null +++ b/src/SDL/i_joy.c @@ -0,0 +1,122 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Joystick handling for Linux + * + *----------------------------------------------------------------------------- + */ + +#ifndef lint +#endif /* lint */ + +#include + +#include "SDL.h" +#include "doomdef.h" +#include "doomtype.h" +#include "m_argv.h" +#include "d_event.h" +#include "d_main.h" +#include "i_joy.h" +#include "lprintf.h" +#include "i_system.h" + +int joyleft; +int joyright; +int joyup; +int joydown; + +int usejoystick; + +#ifdef HAVE_SDL_JOYSTICKGETAXIS +static SDL_Joystick *joystick; +#endif + +static void I_EndJoystick(void) +{ + lprintf(LO_DEBUG, "I_EndJoystick : closing joystick\n"); +} + +void I_PollJoystick(void) +{ +#ifdef HAVE_SDL_JOYSTICKGETAXIS + event_t ev; + Sint16 axis_value; + + if (!usejoystick || (!joystick)) return; + ev.type = ev_joystick; + ev.data1 = + (SDL_JoystickGetButton(joystick, 0)<<0) | + (SDL_JoystickGetButton(joystick, 1)<<1) | + (SDL_JoystickGetButton(joystick, 2)<<2) | + (SDL_JoystickGetButton(joystick, 3)<<3) | + (SDL_JoystickGetButton(joystick, 4)<<4) | + (SDL_JoystickGetButton(joystick, 5)<<5) | + (SDL_JoystickGetButton(joystick, 6)<<6) | + (SDL_JoystickGetButton(joystick, 7)<<7); + axis_value = SDL_JoystickGetAxis(joystick, 0) / 3000; + if (abs(axis_value)<7) axis_value=0; + ev.data2 = axis_value; + axis_value = SDL_JoystickGetAxis(joystick, 1) / 3000; + if (abs(axis_value)<7) axis_value=0; + ev.data3 = axis_value; + + D_PostEvent(&ev); +#endif +} + +void I_InitJoystick(void) +{ +#ifdef HAVE_SDL_JOYSTICKGETAXIS + const char* fname = "I_InitJoystick : "; + int num_joysticks; + + if (!usejoystick) return; + SDL_InitSubSystem(SDL_INIT_JOYSTICK); + num_joysticks=SDL_NumJoysticks(); + if (M_CheckParm("-nojoy") || (usejoystick>num_joysticks) || (usejoystick<0)) { + if ((usejoystick > num_joysticks) || (usejoystick < 0)) + lprintf(LO_WARN, "%sinvalid joystick %d\n", fname, usejoystick); + else + lprintf(LO_INFO, "%suser disabled\n", fname); + return; + } + joystick=SDL_JoystickOpen(usejoystick-1); + if (!joystick) + lprintf(LO_ERROR, "%serror opening joystick %d\n", fname, usejoystick); + else { + I_AtExit(I_EndJoystick, true); + lprintf(LO_INFO, "%sopened %s\n", fname, SDL_JoystickName(joystick)); + joyup = 32767; + joydown = -32768; + joyright = 32767; + joyleft = -32768; + } +#endif +} diff --git a/src/SDL/i_main.c b/src/SDL/i_main.c new file mode 100644 index 0000000..a29ccda --- /dev/null +++ b/src/SDL/i_main.c @@ -0,0 +1,666 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Startup and quit functions. Handles signals, inits the + * memory management, then calls D_DoomMain. Also contains + * I_Init which does other system-related startup stuff. + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +typedef BOOL (WINAPI *SetAffinityFunc)(HANDLE hProcess, DWORD mask); +#else +#include +#endif + +#include + +#include "TEXTSCREEN/txt_main.h" + +#include "doomdef.h" +#include "m_argv.h" +#include "d_main.h" +#include "m_fixed.h" +#include "i_system.h" +#include "i_video.h" +#include "z_zone.h" +#include "lprintf.h" +#include "m_random.h" +#include "doomstat.h" +#include "g_game.h" +#include "m_misc.h" +#include "i_sound.h" +#include "i_main.h" +#include "r_fps.h" +#include "lprintf.h" + +#include +#include +#include + +#include "e6y.h" + +/* Most of the following has been rewritten by Lee Killough + * + * I_GetTime + * killough 4/13/98: Make clock rate adjustable by scale factor + * cphipps - much made static + */ + +static int basetime = 0; + +static int I_GetTime_MS(void) +{ + int ticks = SDL_GetTicks(); + + if (basetime == 0) + basetime = ticks; + + return ticks - basetime; +} + +int ms_to_next_tick; + +int I_GetTime_RealTime (void) +{ + int64_t t = I_GetTime_MS(); + int64_t i = t * TICRATE / 1000; + + ms_to_next_tick = (i + 1) * 1000 / TICRATE - t; + ms_to_next_tick = BETWEEN(0, 1000 / TICRATE, ms_to_next_tick); + + return i; +} + +int realtic_clock_rate = 100; + +static int I_GetTime_Scaled(void) +{ + int64_t t = I_GetTime_MS(); + int64_t i = t * TICRATE * realtic_clock_rate / 100000; + + ms_to_next_tick = (i + 1) * 100000 / realtic_clock_rate / TICRATE - t; + ms_to_next_tick = BETWEEN(0, 100000 / realtic_clock_rate / TICRATE, ms_to_next_tick); + + return i; +} + +static int I_GetTime_FastDemo(void) +{ + static int fasttic; + + ms_to_next_tick = 0; + + return fasttic++; +} + +static int I_GetTime_Error(void) +{ + I_Error("I_GetTime_Error: GetTime() used before initialization"); + return 0; +} + +int (*I_GetTime)(void) = I_GetTime_Error; + +// During a fast demo, no time elapses in between ticks +static int I_TickElapsedTime_FastDemo(void) +{ + return 0; +} + +static int I_TickElapsedTime_RealTime(void) +{ + return (int64_t)I_GetTime_MS() * TICRATE % 1000 * FRACUNIT / 1000; +} + +static int I_TickElapsedTime_Scaled(void) +{ + return (int64_t)I_GetTime_MS() * realtic_clock_rate * TICRATE / 100 % 1000 * FRACUNIT / 1000; +} + +int (*I_TickElapsedTime)(void) = I_TickElapsedTime_RealTime; + +void I_Init(void) +{ + /* killough 4/14/98: Adjustable speedup based on realtic_clock_rate */ + if (fastdemo) + { + I_GetTime = I_GetTime_FastDemo; + I_TickElapsedTime = I_TickElapsedTime_FastDemo; + } + else + if (realtic_clock_rate != 100) + { + I_GetTime = I_GetTime_Scaled; + I_TickElapsedTime = I_TickElapsedTime_Scaled; + } + else + { + I_GetTime = I_GetTime_RealTime; + I_TickElapsedTime = I_TickElapsedTime_RealTime; + } + + { + /* killough 2/21/98: avoid sound initialization if no sound & no music */ + if (!(nomusicparm && nosfxparm)) + I_InitSound(); + } + + R_InitInterpolation(); +} + +//e6y +void I_Init2(void) +{ + if (fastdemo) + { + I_GetTime = I_GetTime_FastDemo; + I_TickElapsedTime = I_TickElapsedTime_FastDemo; + } + else + { + if (realtic_clock_rate != 100) + { + I_GetTime = I_GetTime_Scaled; + I_TickElapsedTime = I_TickElapsedTime_Scaled; + } + else + { + I_GetTime = I_GetTime_RealTime; + I_TickElapsedTime = I_TickElapsedTime_RealTime; + } + } + R_InitInterpolation(); + force_singletics_to = gametic + BACKUPTICS; +} + +/* cleanup handling -- killough: + */ +static void I_SignalHandler(int s) +{ + char buf[2048]; + + signal(s,SIG_IGN); /* Ignore future instances of this signal.*/ + + I_ExeptionProcess(); //e6y + + strcpy(buf,"Exiting on signal: "); + I_SigString(buf+strlen(buf),2000-strlen(buf),s); + + /* If corrupted memory could cause crash, dump memory + * allocation history, which points out probable causes + */ + if (s==SIGSEGV || s==SIGILL || s==SIGFPE) + Z_DumpHistory(buf); + + I_Error("I_SignalHandler: %s", buf); +} + +// +// e6y: exeptions handling +// + +static ExeptionsList_t current_exception_index; + +ExeptionParam_t ExeptionsParams[EXEPTION_MAX + 1] = +{ + {NULL}, + {"gld_CreateScreenSizeFBO: Access violation in glFramebufferTexture2DEXT.\n\n" + "Are you using ATI graphics? Try to update your drivers " + "or change gl_compatibility variable in cfg to 1.\n"}, + {NULL} +}; + +void I_ExeptionBegin(ExeptionsList_t exception_index) +{ + if (current_exception_index == EXEPTION_NONE) + { + current_exception_index = exception_index; + } + else + { + I_Error("I_SignalStateSet: signal_state set!"); + } +} + +void I_ExeptionEnd(void) +{ + current_exception_index = EXEPTION_NONE; +} + +void I_ExeptionProcess(void) +{ + if (current_exception_index > EXEPTION_NONE && current_exception_index < EXEPTION_MAX) + { + I_Error("%s", ExeptionsParams[current_exception_index].error_message); + } +} + + +/* killough 2/22/98: Add support for ENDBOOM, which is PC-specific + * + * this converts BIOS color codes to ANSI codes. + * Its not pretty, but it does the job - rain + * CPhipps - made static + */ + +inline static int convert(int color, int *bold) +{ + if (color > 7) { + color -= 8; + *bold = 1; + } + switch (color) { + case 0: + return 0; + case 1: + return 4; + case 2: + return 2; + case 3: + return 6; + case 4: + return 1; + case 5: + return 5; + case 6: + return 3; + case 7: + return 7; + } + return 0; +} + +/* CPhipps - flags controlling ENDOOM behaviour */ +enum { + endoom_colours = 1, + endoom_nonasciichars = 2, + endoom_droplastline = 4 +}; + +int endoom_mode; + +static void PrintVer(void) +{ + char vbuf[200]; + lprintf(LO_INFO,"%s\n",I_GetVersionString(vbuf,200)); +} + +// +// ENDOOM support using text mode emulation +// +static void I_EndDoom(void) +{ + int lump_eb, lump_ed, lump = -1; + + const unsigned char *endoom_data; + unsigned char *screendata; + +#ifndef _WIN32 + PrintVer(); +#endif + + if (!showendoom || demorecording) + { + return; + } + + /* CPhipps - ENDOOM/ENDBOOM selection */ + lump_eb = W_CheckNumForName("ENDBOOM");/* jff 4/1/98 sign our work */ + lump_ed = W_CheckNumForName("ENDOOM"); /* CPhipps - also maybe ENDOOM */ + + if (lump_eb == -1) + lump = lump_ed; + else if (lump_ed == -1) + lump = lump_eb; + else + { /* Both ENDOOM and ENDBOOM are present */ +#define LUMP_IS_NEW(num) (!((lumpinfo[num].source == source_iwad) || (lumpinfo[num].source == source_auto_load))) + switch ((LUMP_IS_NEW(lump_ed) ? 1 : 0 ) | + (LUMP_IS_NEW(lump_eb) ? 2 : 0)) { + case 1: + lump = lump_ed; + break; + case 2: + lump = lump_eb; + break; + default: + /* Both lumps have equal priority, both present */ + lump = (P_Random(pr_misc) & 1) ? lump_ed : lump_eb; + break; + } + } + + if (lump != -1) + { + endoom_data = (const unsigned char *)W_CacheLumpNum(lump); + + // Set up text mode screen + TXT_Init(); + + // Make sure the new window has the right title and icon + I_SetWindowCaption(); + I_SetWindowIcon(); + + // Write the data to the screen memory + screendata = TXT_GetScreenData(); + memcpy(screendata, endoom_data, 4000); + + // Wait for a keypress + while (true) + { + TXT_UpdateScreen(); + + if (TXT_GetChar() > 0) + { + break; + } + + TXT_Sleep(0); + } + + // Shut down text mode screen + TXT_Shutdown(); + } +} + +// 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) +// Copyright(C) 2005-2014 Simon Howard + +typedef struct atexit_listentry_s atexit_listentry_t; + +struct atexit_listentry_s +{ + atexit_func_t func; + dboolean run_on_error; + atexit_listentry_t *next; +}; + +static atexit_listentry_t *exit_funcs = NULL; + +void I_AtExit(atexit_func_t func, dboolean 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; +} + +/* I_SafeExit + * This function is called instead of exit() by functions that might be called + * during the exit process (i.e. after exit() has already been called) + * Prevent infinitely recursive exits -- killough + */ + +void I_SafeExit(int rc) +{ + atexit_listentry_t *entry; + + // Run through all exit functions + + while ((entry = exit_funcs)) + { + exit_funcs = exit_funcs->next; + + if (rc == 0 || entry->run_on_error) + { + entry->func(); + } + } + + exit(rc); +} + +static void I_Quit (void) +{ + if (!demorecording) + I_EndDoom(); + if (demorecording) + G_CheckDemoStatus(); + M_SaveDefaults (); + I_DemoExShutdown(); +} + +#ifdef SECURE_UID +uid_t stored_euid = -1; +#endif + +// +// Ability to use only the allowed CPUs +// + +static void I_SetAffinityMask(void) +{ + // Forcing single core only for "SDL MIDI Player" + process_affinity_mask = 0; + if (!strcasecmp(snd_midiplayer, midiplayers[midi_player_sdl])) + { + process_affinity_mask = 1; + } + + // Set the process affinity mask so that all threads + // run on the same processor. This is a workaround for a bug in + // SDL_mixer that causes occasional crashes. + if (process_affinity_mask) + { + const char *errbuf = NULL; +#ifdef _WIN32 + HMODULE kernel32_dll; + SetAffinityFunc SetAffinity = NULL; + int ok = false; + + // Find the kernel interface DLL. + kernel32_dll = LoadLibrary("kernel32.dll"); + + if (kernel32_dll) + { + // Find the SetProcessAffinityMask function. + SetAffinity = (SetAffinityFunc)GetProcAddress(kernel32_dll, "SetProcessAffinityMask"); + + // If the function was not found, we are on an old (Win9x) system + // that doesn't have this function. That's no problem, because + // those systems don't support SMP anyway. + + if (SetAffinity) + { + ok = SetAffinity(GetCurrentProcess(), process_affinity_mask); + } + } + + if (!ok) + { + errbuf = WINError(); + } +#elif defined(HAVE_SCHED_SETAFFINITY) + // POSIX version: + int i; + { + cpu_set_t set; + + CPU_ZERO(&set); + + for(i = 0; i < 16; i++) + { + CPU_SET((process_affinity_mask>>i)&1, &set); + } + + if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) + { + errbuf = strerror(errno); + } + } +#else + return; +#endif + + if (errbuf == NULL) + { + lprintf(LO_INFO, "I_SetAffinityMask: manual affinity mask is %d\n", process_affinity_mask); + } + else + { + lprintf(LO_ERROR, "I_SetAffinityMask: failed to set process affinity mask (%s)\n", errbuf); + } + } +} + +// +// Sets the priority class for the prboom-plus process +// + +void I_SetProcessPriority(void) +{ + if (process_priority) + { + const char *errbuf = NULL; + +#ifdef _WIN32 + { + DWORD dwPriorityClass = NORMAL_PRIORITY_CLASS; + + if (process_priority == 1) + dwPriorityClass = HIGH_PRIORITY_CLASS; + else if (process_priority == 2) + dwPriorityClass = REALTIME_PRIORITY_CLASS; + + if (SetPriorityClass(GetCurrentProcess(), dwPriorityClass) == 0) + { + errbuf = WINError(); + } + } +#else + return; +#endif + + if (errbuf == NULL) + { + lprintf(LO_INFO, "I_SetProcessPriority: priority for the process is %d\n", process_priority); + } + else + { + lprintf(LO_ERROR, "I_SetProcessPriority: failed to set priority for the process (%s)\n", errbuf); + } + } +} + +//int main(int argc, const char * const * argv) +int main(int argc, char **argv) +{ +#ifdef SECURE_UID + /* First thing, revoke setuid status (if any) */ + stored_euid = geteuid(); + if (getuid() != stored_euid) + if (seteuid(getuid()) < 0) + fprintf(stderr, "Failed to revoke setuid\n"); + else + fprintf(stderr, "Revoked uid %d\n",stored_euid); +#endif + + myargc = argc; + myargv = (char**)malloc(sizeof(myargv[0]) * myargc); + memcpy(myargv, argv, sizeof(myargv[0]) * myargc); + + // e6y: Check for conflicts. + // Conflicting command-line parameters could cause the engine to be confused + // in some cases. Added checks to prevent this. + // Example: glboom.exe -record mydemo -playdemo demoname + ParamsMatchingCheck(); + + // e6y: was moved from D_DoomMainSetup + // init subsystems + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"M_LoadDefaults: Load system defaults.\n"); + M_LoadDefaults(); // load before initing other systems + + /* Version info */ + lprintf(LO_INFO,"\n"); + PrintVer(); + + /* cph - Z_Close must be done after I_Quit, so we register it first. */ + I_AtExit(Z_Close, true); + /* + killough 1/98: + + This fixes some problems with exit handling + during abnormal situations. + + The old code called I_Quit() to end program, + while now I_Quit() is installed as an exit + handler and exit() is called to exit, either + normally or abnormally. Seg faults are caught + and the error handler is used, to prevent + being left in graphics mode or having very + loud SFX noise because the sound card is + left in an unstable state. + */ + + Z_Init(); /* 1/18/98 killough: start up memory stuff first */ + + I_AtExit(I_Quit, false); +#ifndef PRBOOM_DEBUG + if (!M_CheckParm("-devparm")) + { + signal(SIGSEGV, I_SignalHandler); + } + signal(SIGTERM, I_SignalHandler); + signal(SIGFPE, I_SignalHandler); + signal(SIGILL, I_SignalHandler); + signal(SIGINT, I_SignalHandler); /* killough 3/6/98: allow CTRL-BRK during init */ + signal(SIGABRT, I_SignalHandler); +#endif + + // Ability to use only the allowed CPUs + I_SetAffinityMask(); + + // Priority class for the prboom-plus process + I_SetProcessPriority(); + + /* cphipps - call to video specific startup code */ + I_PreInitGraphics(); + + D_DoomMain (); + return 0; +} diff --git a/src/SDL/i_network.c b/src/SDL/i_network.c new file mode 100644 index 0000000..835ea27 --- /dev/null +++ b/src/SDL/i_network.c @@ -0,0 +1,292 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Low level UDP network interface. This is shared between the server + * and client, with SERVER defined for the former to select some extra + * functions. Handles socket creation, and packet send and receive. + * + *-----------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include + +#ifdef HAVE_NET + +#include "SDL.h" +#include "SDL_net.h" + +#include "protocol.h" +#include "i_network.h" +#include "lprintf.h" +#ifndef PRBOOM_SERVER +#include "i_system.h" +#endif +//#include "doomstat.h" + +/* cph - + * Each client will either use the IPv4 socket or the IPv6 socket + * Each server will use whichever or both that are available + */ +UDP_CHANNEL sentfrom; +IPaddress sentfrom_addr; +UDP_SOCKET udp_socket; + +/* Statistics */ +size_t sentbytes, recvdbytes; + +UDP_PACKET *udp_packet; + +/* I_ShutdownNetwork + * + * Shutdown the network code + */ +void I_ShutdownNetwork(void) +{ + SDLNet_FreePacket(udp_packet); + SDLNet_Quit(); +} + +/* I_InitNetwork + * + * Sets up the network code + */ +void I_InitNetwork(void) +{ + SDLNet_Init(); +#ifndef PRBOOM_SERVER + I_AtExit(I_ShutdownNetwork, true); +#else + atexit(I_ShutdownNetwork); +#endif + udp_packet = SDLNet_AllocPacket(10000); +} + +UDP_PACKET *I_AllocPacket(int size) +{ + return(SDLNet_AllocPacket(size)); +} + +void I_FreePacket(UDP_PACKET *packet) +{ + SDLNet_FreePacket(packet); +} + + +/* cph - I_WaitForPacket - use select(2) via SDL_net's interface + * No more I_uSleep loop kludge */ + +void I_WaitForPacket(int ms) +{ + SDLNet_SocketSet ss = SDLNet_AllocSocketSet(1); + SDLNet_UDP_AddSocket(ss, udp_socket); + SDLNet_CheckSockets(ss,ms); + SDLNet_FreeSocketSet(ss); +} + +/* I_ConnectToServer + * + * Connect to a server + */ +IPaddress serverIP; + +int I_ConnectToServer(const char *serv) +{ + char server[500], *p; + Uint16 port; + + /* Split serv into address and port */ + if (strlen(serv)>500) return 0; + strcpy(server,serv); + p = strchr(server, ':'); + if(p) + { + *p++ = '\0'; + port = atoi(p); + } + else + port = 5030; /* Default server port */ + + SDLNet_ResolveHost(&serverIP, server, port); + if ( serverIP.host == INADDR_NONE ) + return -1; + + if (SDLNet_UDP_Bind(udp_socket, 0, &serverIP) == -1) + return -1; + + return 0; +} + +/* I_Disconnect + * + * Disconnect from server + */ +void I_Disconnect(void) +{ +/* int i; + UDP_PACKET *packet; + packet_header_t *pdata = (packet_header_t *)packet->data; + packet = I_AllocPacket(sizeof(packet_header_t) + 1); + + packet->data[sizeof(packet_header_t)] = consoleplayer; + pdata->type = PKT_QUIT; pdata->tic = gametic; + + for (i=0; i<4; i++) { + I_SendPacket(packet); + I_uSleep(10000); + } + I_FreePacket(packet);*/ + SDLNet_UDP_Unbind(udp_socket, 0); +} + +/* + * I_Socket + * + * Sets the given socket non-blocking, binds to the given port, or first + * available if none is given + */ +UDP_SOCKET I_Socket(Uint16 port) +{ + if(port) + return (SDLNet_UDP_Open(port)); + else { + UDP_SOCKET sock; + port = IPPORT_RESERVED; + while( (sock = SDLNet_UDP_Open(port)) == NULL ) + port++; + return sock; + } +} + +void I_CloseSocket(UDP_SOCKET sock) +{ + SDLNet_UDP_Close(sock); +} + +UDP_CHANNEL I_RegisterPlayer(IPaddress *ipaddr) +{ + static int freechannel; + return(SDLNet_UDP_Bind(udp_socket, freechannel++, ipaddr)); +} + +void I_UnRegisterPlayer(UDP_CHANNEL channel) +{ + SDLNet_UDP_Unbind(udp_socket, channel); +} + +/* + * ChecksumPacket + * + * Returns the checksum of a given network packet + */ +static byte ChecksumPacket(const packet_header_t* buffer, size_t len) +{ + const byte* p = (const byte*)buffer; + byte sum = 0; + + if (len==0) + return 0; + + while (p++, --len) + sum += *p; + + return sum; +} + +size_t I_GetPacket(packet_header_t* buffer, size_t buflen) +{ + int checksum; + size_t len; + int status; + + status = SDLNet_UDP_Recv(udp_socket, udp_packet); + len = udp_packet->len; + if (buflen0) ) + memcpy(buffer, udp_packet->data, len); + sentfrom=udp_packet->channel; + sentfrom_addr=udp_packet->address; + checksum=buffer->checksum; + buffer->checksum=0; + if ( (status!=0) && (len>0)) { + byte psum = ChecksumPacket(buffer, len); // https://logicaltrust.net/blog/2019/10/prboom1.html +/* fprintf(stderr, "recvlen = %u, stolen = %u, csum = %u, psum = %u\n", + udp_packet->len, len, checksum, psum); */ + if (psum == checksum) return len; + } + return 0; +} + +void I_SendPacket(packet_header_t* packet, size_t len) +{ + packet->checksum = ChecksumPacket(packet, len); + memcpy(udp_packet->data, packet, udp_packet->len = len); + SDLNet_UDP_Send(udp_socket, 0, udp_packet); +} + +void I_SendPacketTo(packet_header_t* packet, size_t len, UDP_CHANNEL *to) +{ + packet->checksum = ChecksumPacket(packet, len); + memcpy(udp_packet->data, packet, udp_packet->len = len); + SDLNet_UDP_Send(udp_socket, *to, udp_packet); +} + +void I_PrintAddress(FILE* fp, UDP_CHANNEL *addr) +{ +/* + char *addy; + Uint16 port; + IPaddress *address; + + address = SDLNet_UDP_GetPeerAddress(udp_socket, player); + +//FIXME: if it cant resolv it may freeze up + addy = SDLNet_ResolveIP(address); + port = address->port; + + if(addy != NULL) + fprintf(fp, "%s:%d", addy, port); + else + fprintf(fp, "Error"); +*/ +} + +#endif /* HAVE_NET */ diff --git a/src/SDL/i_sound.c b/src/SDL/i_sound.c new file mode 100644 index 0000000..2d527df --- /dev/null +++ b/src/SDL/i_sound.c @@ -0,0 +1,1732 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System interface for sound. + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_LIBSDL2_MIXER +#define HAVE_MIXER +#endif + +#include "SDL.h" +#include "SDL_audio.h" +#include "SDL_mutex.h" + +#include "SDL_endian.h" + +#include "SDL_version.h" +#include "SDL_thread.h" +#ifdef HAVE_MIXER +#define USE_RWOPS +#include "SDL_mixer.h" +#endif + +#include "z_zone.h" + +#include "m_swap.h" +#include "i_sound.h" +#include "m_argv.h" +#include "m_misc.h" +#include "w_wad.h" +#include "lprintf.h" +#include "s_sound.h" + +#include "doomdef.h" +#include "doomstat.h" +#include "doomtype.h" + +#include "d_main.h" +#include "i_system.h" + +//e6y +#include "i_pcsound.h" +#include "e6y.h" + +int snd_pcspeaker; +int lowpass_filter; + +// The number of internal mixing channels, +// the samples calculated for each mixing step, +// the size of the 16bit, 2 hardware channel (stereo) +// mixing buffer, and the samplerate of the raw data. + +// Variables used by Boom from Allegro +// created here to avoid changes to core Boom files +int snd_card = 1; +int mus_card = 1; +int detect_voices = 0; // God knows + +static dboolean sound_inited = false; +static dboolean first_sound_init = true; + +// MWM 2000-01-08: Sample rate in samples/second +int snd_samplerate = 11025; +int snd_samplecount = 512; + +// The actual output device. +int audio_fd; + +typedef struct +{ + // SFX id of the playing sound effect. + // Used to catch duplicates (like chainsaw). + int id; + // The channel step amount... + unsigned int step; + // ... and a 0.16 bit remainder of last step. + unsigned int stepremainder; + unsigned int samplerate; + unsigned int bits; + float alpha; + int prevS; + // The channel data pointers, start and end. + const unsigned char *data; + const unsigned char *enddata; + // Time/gametic that the channel started playing, + // used to determine oldest, which automatically + // has lowest priority. + // In case number of active sounds exceeds + // available channels. + int starttime; + // left and right channel volume (0-127) + int leftvol; + int rightvol; +} channel_info_t; + +channel_info_t channelinfo[MAX_CHANNELS]; + +// Pitch to stepping lookup, unused. +int steptable[256]; + +// Volume lookups. +//int vol_lookup[128 * 256]; + +// NSM +static int dumping_sound = 0; + + +// lock for updating any params related to sfx +SDL_mutex *sfxmutex; +// lock for updating any params related to music +SDL_mutex *musmutex; + + +/* cph + * stopchan + * Stops a sound, unlocks the data + */ + +static void stopchan(int i) +{ + if (channelinfo[i].data) /* cph - prevent excess unlocks */ + { + channelinfo[i].data = NULL; + } +} + +// +// This function adds a sound to the +// list of currently active sounds, +// which is maintained as a given number +// (eight, usually) of internal channels. +// Returns a handle. +// +static int addsfx(int sfxid, int channel, const unsigned char *data, size_t len) +{ + channel_info_t *const ci = &channelinfo[channel]; + + stopchan(channel); + + if (strncmp(data, "RIFF", 4) == 0 && strncmp(data + 8, "WAVEfmt ", 8) == 0) + { + // FIXME: can't handle stereo wavs + // ci->channels = data[22] | (data[23] << 8); + ci->samplerate = data[24] | (data[25] << 8) | (data[26] << 16) + | (data[27] << 24); + ci->bits = data[34] | (data[35] << 8); + ci->data = data + 44; + ci->enddata = data + 44 + (data[40] | (data[41] << 8) | (data[42] << 16) + | (data[43] << 24)); + if (ci->enddata > data + len - 2) + ci->enddata = data + len - 2; + } + else + { + ci->samplerate = (data[3] << 8) + data[2]; + ci->bits = 8; + ci->data = data + 8; + ci->enddata = data + len - 1; + } + + ci->prevS = 0; + + // Filter from chocolate doom i_sdlsound.c 682-695 + // Low-pass filter for cutoff frequency f: + // + // For sampling rate r, dt = 1 / r + // rc = 1 / 2*pi*f + // alpha = dt / (rc + dt) + + // Filter to the half sample rate of the original sound effect + // (maximum frequency, by nyquist) + + if (lowpass_filter) + { + float rc, dt; + dt = 1.0f / snd_samplerate; + rc = 1.0f / (3.14f * ci->samplerate); + ci->alpha = dt / (rc + dt); + } + + ci->stepremainder = 0; + // Should be gametic, I presume. + ci->starttime = gametic; + + // Preserve sound SFX id, + // e.g. for avoiding duplicates of chainsaw. + ci->id = sfxid; + + return channel; +} + +static int getSliceSize(void) +{ + int limit, n; + + if (snd_samplecount >= 32) + return snd_samplecount * snd_samplerate / 11025; + + limit = snd_samplerate / TICRATE; + + // Try all powers of two, not exceeding the limit. + + for (n=0;; ++n) + { + // 2^n <= limit < 2^n+1 ? + + if ((1 << (n + 1)) > limit) + { + return (1 << n); + } + } + + // Should never happen? + + return 1024; +} + +static void updateSoundParams(int handle, int volume, int seperation, int pitch) +{ + int slot = handle; + int rightvol; + int leftvol; + +#ifdef RANGECHECK + if ((handle < 0) || (handle >= MAX_CHANNELS)) + I_Error("I_UpdateSoundParams: handle out of range"); +#endif + + if (snd_pcspeaker) + return; + + // Set stepping + // MWM 2000-12-24: Calculates proportion of channel samplerate + // to global samplerate for mixing purposes. + // Patched to shift left *then* divide, to minimize roundoff errors + // as well as to use SAMPLERATE as defined above, not to assume 11025 Hz + if (pitched_sounds) + channelinfo[slot].step = (unsigned int)(((uint64_t)channelinfo[slot].samplerate * steptable[pitch]) / snd_samplerate); + else + channelinfo[slot].step = ((channelinfo[slot].samplerate << 16) / snd_samplerate); + + // Separation, that is, orientation/stereo. + // range is: 1 - 256 + seperation += 1; + + // Per left/right channel. + // x^2 seperation, + // adjust volume properly. + leftvol = volume - ((volume * seperation * seperation) >> 16); + seperation = seperation - 257; + rightvol = volume - ((volume * seperation * seperation) >> 16); + + // Sanity check, clamp volume. + if (rightvol < 0 || rightvol > 127) + I_Error("rightvol out of bounds"); + + if (leftvol < 0 || leftvol > 127) + I_Error("leftvol out of bounds"); + + // Get the proper lookup table piece + // for this volume level??? + channelinfo[slot].leftvol = leftvol; + channelinfo[slot].rightvol = rightvol; +} + +void I_UpdateSoundParams(int handle, int volume, int seperation, int pitch) +{ + SDL_LockMutex (sfxmutex); + updateSoundParams(handle, volume, seperation, pitch); + SDL_UnlockMutex (sfxmutex); +} + +// +// SFX API +// Note: this was called by S_Init. +// However, whatever they did in the +// old DPMS based DOS version, this +// were simply dummies in the Linux +// version. +// See soundserver initdata(). +// +void I_SetChannels(void) +{ + // Init internal lookups (raw data, mixing buffer, channels). + // This function sets up internal lookups used during + // the mixing process. + int i; + //int j; + + int *steptablemid = steptable + 128; + + // Okay, reset internal mixing channels to zero. + for (i = 0; i < MAX_CHANNELS; i++) + { + memset(&channelinfo[i], 0, sizeof(channel_info_t)); + } + + // This table provides step widths for pitch parameters. + // I fail to see that this is currently used. + for (i = -128 ; i < 128 ; i++) + steptablemid[i] = (int)(pow(1.2, (double)i / 64.0) * 65536.0); + + + // Generates volume lookup tables + // which also turn the unsigned samples + // into signed samples. + /* + for (i = 0 ; i < 128 ; i++) + for (j = 0 ; j < 256 ; j++) + { + // proff - made this a little bit softer, because with + // full volume the sound clipped badly + vol_lookup[i * 256 + j] = (i * (j - 128) * 256) / 191; + //vol_lookup[i*256+j] = (i*(j-128)*256)/127; + } + */ +} + +// +// Retrieve the raw data lump index +// for a given SFX name. +// +int I_GetSfxLumpNum(sfxinfo_t *sfx) +{ + char namebuf[9]; + const char *prefix; + + // Different prefix for PC speaker sound effects. + prefix = (snd_pcspeaker ? "dp" : "ds"); + + sprintf(namebuf, "%s%s", prefix, sfx->name); + return W_CheckNumForName(namebuf); //e6y: make missing sounds non-fatal +} + +// +// Starting a sound means adding it +// to the current list of active sounds +// in the internal channels. +// As the SFX info struct contains +// e.g. a pointer to the raw data, +// it is ignored. +// As our sound handling does not handle +// priority, it is ignored. +// Pitching (that is, increased speed of playback) +// is set, but currently not used by mixing. +// +int I_StartSound(int id, int channel, int vol, int sep, int pitch, int priority) +{ + const unsigned char *data; + int lump; + size_t len; + + if ((channel < 0) || (channel >= MAX_CHANNELS)) +#ifdef RANGECHECK + I_Error("I_StartSound: handle out of range"); +#else + return -1; +#endif + + if (snd_pcspeaker) + return I_PCS_StartSound(id, channel, vol, sep, pitch, priority); + + lump = S_sfx[id].lumpnum; + + // We will handle the new SFX. + // Set pointer to raw data. + len = W_LumpLength(lump); + + // e6y: Crash with zero-length sounds. + // Example wad: dakills (http://www.doomworld.com/idgames/index.php?id=2803) + // The entries DSBSPWLK, DSBSPACT, DSSWTCHN and DSSWTCHX are all zero-length sounds + if (len <= 8) return -1; + + /* Find padded length */ + len -= 8; + // do the lump caching outside the SDL_LockAudio/SDL_UnlockAudio pair + // use locking which makes sure the sound data is in a malloced area and + // not in a memory mapped one + data = (const unsigned char *)W_LockLumpNum(lump); + + SDL_LockMutex (sfxmutex); + + // Returns a handle (not used). + addsfx(id, channel, data, len); + updateSoundParams(channel, vol, sep, pitch); + + SDL_UnlockMutex (sfxmutex); + + + return channel; +} + + + +void I_StopSound (int handle) +{ +#ifdef RANGECHECK + if ((handle < 0) || (handle >= MAX_CHANNELS)) + I_Error("I_StopSound: handle out of range"); +#endif + + if (snd_pcspeaker) + { + I_PCS_StopSound(handle); + return; + } + + SDL_LockMutex (sfxmutex); + stopchan(handle); + SDL_UnlockMutex (sfxmutex); +} + + +dboolean I_SoundIsPlaying(int handle) +{ +#ifdef RANGECHECK + if ((handle < 0) || (handle >= MAX_CHANNELS)) + I_Error("I_SoundIsPlaying: handle out of range"); +#endif + + if (snd_pcspeaker) + return I_PCS_SoundIsPlaying(handle); + + return channelinfo[handle].data != NULL; +} + + +dboolean I_AnySoundStillPlaying(void) +{ + dboolean result = false; + int i; + + if (snd_pcspeaker) + return false; + + for (i = 0; i < MAX_CHANNELS; i++) + result |= channelinfo[i].data != NULL; + + return result; +} + + +// +// This function loops all active (internal) sound +// channels, retrieves a given number of samples +// from the raw sound data, modifies it according +// to the current (internal) channel parameters, +// mixes the per channel samples into the given +// mixing buffer, and clamping it to the allowed +// range. +// +// This function currently supports only 16bit. +// + +#ifndef HAVE_OWN_MUSIC +static void Exp_UpdateMusic (void *buff, unsigned nsamp); +#endif + +// from pcsound_sdl.c +void PCSound_Mix_Callback(void *udata, Uint8 *stream, int len); + +static void I_UpdateSound(void *unused, Uint8 *stream, int len) +{ + // Mix current sound data. + // Data, from raw sound, for right and left. + // register unsigned char sample; + register int dl; + register int dr; + + // Pointers in audio stream, left, right, end. + signed short *leftout; + signed short *rightout; + signed short *leftend; + // Step in stream, left and right, thus two. + int step; + + // Mixing channel index. + int chan; + + if (snd_midiplayer == NULL) // This is but a temporary fix. Please do remove after a more definitive one! + memset(stream, 0, len); + + // NSM: when dumping sound, ignore the callback calls and only + // service dumping calls + if (dumping_sound && unused != (void *) 0xdeadbeef) + return; + +#ifndef HAVE_OWN_MUSIC + // do music update + if (use_experimental_music) + { + SDL_LockMutex (musmutex); + Exp_UpdateMusic (stream, len / 4); + SDL_UnlockMutex (musmutex); + } +#endif + + if (snd_pcspeaker) + { + PCSound_Mix_Callback (NULL, stream, len); + // no sfx mixing + return; + } + + SDL_LockMutex (sfxmutex); + // Left and right channel + // are in audio stream, alternating. + leftout = (signed short *)stream; + rightout = ((signed short *)stream) + 1; + step = 2; + + // Determine end, for left channel only + // (right channel is implicit). + leftend = leftout + (len / 4) * step; + + // Mix sounds into the mixing buffer. + // Loop over step*SAMPLECOUNT, + // that is 512 values for two channels. + while (leftout != leftend) + { + // Reset left/right value. + //dl = 0; + //dr = 0; + dl = *leftout; + dr = *rightout; + + // Love thy L2 chache - made this a loop. + // Now more channels could be set at compile time + // as well. Thus loop those channels. + for ( chan = 0; chan < numChannels; chan++ ) + { + channel_info_t *ci = channelinfo + chan; + + // Check channel, if active. + if (ci->data) + { + int s; + // Get the raw data from the channel. + // no filtering + //s = ci->data[0] * 0x10000 - 0x800000; + + // linear filtering + // the old SRC did linear interpolation back into 8 bit, and then expanded to 16 bit. + // this does interpolation and 8->16 at same time, allowing slightly higher quality + if (ci->bits == 16) + { + s = (short)(ci->data[0] | (ci->data[1] << 8)) * (255 - (ci->stepremainder >> 8)) + + (short)(ci->data[2] | (ci->data[3] << 8)) * (ci->stepremainder >> 8); + } + else + { + s = (ci->data[0] * (0x10000 - ci->stepremainder)) + + (ci->data[1] * (ci->stepremainder)) + - 0x800000; // convert to signed + } + + // lowpass + if (lowpass_filter) + { + s = ci->prevS + ci->alpha * (s - ci->prevS); + ci->prevS = s; + } + + // Add left and right part + // for this channel (sound) + // to the current data. + // Adjust volume accordingly. + + // full loudness (vol=127) is actually 127/191 + + dl += ci->leftvol * s / 49152; // >> 15; + dr += ci->rightvol * s / 49152; // >> 15; + + // Increment index ??? + ci->stepremainder += ci->step; + + // MSB is next sample??? + if (ci->bits == 16) + ci->data += (ci->stepremainder >> 16) * 2; + else + ci->data += ci->stepremainder >> 16; + + // Limit to LSB??? + ci->stepremainder &= 0xffff; + + // Check whether we are done. + if (ci->data >= ci->enddata) + stopchan(chan); + } + } + + // Clamp to range. Left hardware channel. + // Has been char instead of short. + // if (dl > 127) *leftout = 127; + // else if (dl < -128) *leftout = -128; + // else *leftout = dl; + + if (dl > SHRT_MAX) + *leftout = SHRT_MAX; + else if (dl < SHRT_MIN) + *leftout = SHRT_MIN; + else + *leftout = (signed short)dl; + + // Same for right hardware channel. + if (dr > SHRT_MAX) + *rightout = SHRT_MAX; + else if (dr < SHRT_MIN) + *rightout = SHRT_MIN; + else + *rightout = (signed short)dr; + + // Increment current pointers in stream + leftout += step; + rightout += step; + } + SDL_UnlockMutex (sfxmutex); +} + +void I_ShutdownSound(void) +{ + if (sound_inited) + { + lprintf(LO_INFO, "I_ShutdownSound: "); +#ifdef HAVE_MIXER + Mix_CloseAudio(); +#endif + SDL_CloseAudio(); + lprintf(LO_INFO, "\n"); + sound_inited = false; + + if (sfxmutex) + { + SDL_DestroyMutex (sfxmutex); + sfxmutex = NULL; + } + } +} + +//static SDL_AudioSpec audio; + +void I_InitSound(void) +{ + int audio_rate; + int audio_channels; + int audio_buffers; + SDL_AudioSpec audio; + + // haleyjd: the docs say we should do this + if (SDL_InitSubSystem(SDL_INIT_AUDIO)) + { + lprintf(LO_INFO, "Couldn't initialize SDL audio (%s))\n", SDL_GetError()); + nosfxparm = true; + nomusicparm = true; + return; + } + if (sound_inited) + I_ShutdownSound(); + + // Secure and configure sound device first. + lprintf(LO_INFO, "I_InitSound: "); + + if (!use_experimental_music) + { +#ifdef HAVE_MIXER + + /* Initialize variables */ + audio_rate = snd_samplerate; + audio_channels = 2; + audio_buffers = getSliceSize(); + + if (Mix_OpenAudioDevice(audio_rate, MIX_DEFAULT_FORMAT, audio_channels, audio_buffers, + NULL, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) < 0) + { + lprintf(LO_INFO,"couldn't open audio with desired format (%s)\n", SDL_GetError()); + nosfxparm = true; + nomusicparm = true; + return; + } + // [FG] feed actual sample frequency back into config variable + Mix_QuerySpec(&snd_samplerate, NULL, NULL); + sound_inited_once = true;//e6y + sound_inited = true; + Mix_SetPostMix(I_UpdateSound, NULL); + lprintf(LO_INFO," configured audio device with %d samples/slice\n", audio_buffers); + } + else +#else // HAVE_MIXER + } +#endif // HAVE_MIXER + { + // Open the audio device + audio.freq = snd_samplerate; +#if ( SDL_BYTEORDER == SDL_BIG_ENDIAN ) + audio.format = AUDIO_S16MSB; +#else + audio.format = AUDIO_S16LSB; +#endif + audio.channels = 2; + audio.samples = getSliceSize(); + audio.callback = I_UpdateSound; + if ( SDL_OpenAudio(&audio, NULL) < 0 ) + { + lprintf(LO_INFO, "couldn't open audio with desired format (%s))\n", SDL_GetError()); + nosfxparm = true; + nomusicparm = true; + return; + } + sound_inited_once = true;//e6y + sound_inited = true; + lprintf(LO_INFO, " configured audio device with %d samples/slice\n", audio.samples); + } + if (first_sound_init) + { + I_AtExit(I_ShutdownSound, true); + first_sound_init = false; + } + + sfxmutex = SDL_CreateMutex (); + + // If we are using the PC speaker, we now need to initialise it. + if (snd_pcspeaker) + I_PCS_InitSound(); + + if (!nomusicparm) + I_InitMusic(); + + // Finished initialization. + lprintf(LO_INFO, "I_InitSound: sound module ready\n"); + SDL_PauseAudio(0); +} + + +// NSM sound capture routines + +// silences sound output, and instead allows sound capture to work +// call this before sound startup +void I_SetSoundCap (void) +{ + dumping_sound = 1; +} + +// grabs len samples of audio (16 bit interleaved) +unsigned char *I_GrabSound (int len) +{ + static unsigned char *buffer = NULL; + static size_t buffer_size = 0; + size_t size; + + if (!dumping_sound) + return NULL; + + size = len * 4; + if (!buffer || size > buffer_size) + { + buffer_size = size * 4; + buffer = (unsigned char *)realloc (buffer, buffer_size); + } + + if (buffer) + { + memset (buffer, 0, size); + I_UpdateSound ((void *) 0xdeadbeef, buffer, size); + } + return buffer; +} + + + + +// NSM helper routine for some of the streaming audio +void I_ResampleStream (void *dest, unsigned nsamp, void (*proc) (void *dest, unsigned nsamp), unsigned sratein, unsigned srateout) +{ // assumes 16 bit signed interleaved stereo + + unsigned i; + int j = 0; + + short *sout = (short*)dest; + + static short *sin = NULL; + static unsigned sinsamp = 0; + + static unsigned remainder = 0; + unsigned step = (sratein << 16) / (unsigned) srateout; + + unsigned nreq = (step * nsamp + remainder) >> 16; + + if (nreq > sinsamp) + { + sin = (short*)realloc (sin, (nreq + 1) * 4); + if (!sinsamp) // avoid pop when first starting stream + sin[0] = sin[1] = 0; + sinsamp = nreq; + } + + proc (sin + 2, nreq); + + for (i = 0; i < nsamp; i++) + { + *sout++ = ((unsigned) sin[j + 0] * (0x10000 - remainder) + + (unsigned) sin[j + 2] * remainder) >> 16; + *sout++ = ((unsigned) sin[j + 1] * (0x10000 - remainder) + + (unsigned) sin[j + 3] * remainder) >> 16; + remainder += step; + j += remainder >> 16 << 1; + remainder &= 0xffff; + } + sin[0] = sin[nreq * 2]; + sin[1] = sin[nreq * 2 + 1]; +} + + +#ifndef HAVE_OWN_MUSIC + +// +// MUSIC API. +// + +int use_experimental_music = -1; + +static void Exp_UpdateMusic (void *buff, unsigned nsamp); +static int Exp_RegisterMusic (const char *filename, musicinfo_t *song); +static int Exp_RegisterSong (const void *data, size_t len); +static int Exp_RegisterSongEx (const void *data, size_t len, int try_mus2mid); +static void Exp_SetMusicVolume (int volume); +static void Exp_UnRegisterSong(int handle); +static void Exp_StopSong(int handle); +static void Exp_ResumeSong (int handle); +static void Exp_PauseSong (int handle); +static void Exp_PlaySong(int handle, int looping); +static void Exp_InitMusic(void); +static void Exp_ShutdownMusic(void); + + + + + +#ifdef HAVE_MIXER + +#include "mus2mid.h" + + +static Mix_Music *music[2] = { NULL, NULL }; + +// Some tracks are directly streamed from the RWops; +// we need to free them in the end +static SDL_RWops *rw_midi = NULL; + +static char *music_tmp = NULL; /* cph - name of music temporary file */ + +// List of extensions that can be appended to music_tmp. First must be "". +static const char *music_tmp_ext[] = { "", ".mp3", ".ogg" }; +#define MUSIC_TMP_EXT (sizeof(music_tmp_ext)/sizeof(*music_tmp_ext)) + +#endif + +void I_ShutdownMusic(void) +{ + if (use_experimental_music) + { + Exp_ShutdownMusic (); + return; + } +#ifdef HAVE_MIXER + if (music_tmp) { + int i; + char *name; + + S_StopMusic(); + for (i = 0; i < MUSIC_TMP_EXT; i++) + { + name = (char*)malloc(strlen(music_tmp) + strlen(music_tmp_ext[i]) + 1); + sprintf(name, "%s%s", music_tmp, music_tmp_ext[i]); + if (!unlink(name)) + lprintf(LO_DEBUG, "I_ShutdownMusic: removed %s\n", name); + free(name); + } + free(music_tmp); + music_tmp = NULL; + } +#endif +} + +void I_InitMusic(void) +{ + if (use_experimental_music) + { + Exp_InitMusic (); + return; + } +#ifdef HAVE_MIXER + if (!music_tmp) { +#ifndef _WIN32 + music_tmp = strdup("/tmp/"PACKAGE_TARNAME"-music-XXXXXX"); + { + int fd = mkstemp(music_tmp); + if (fd<0) { + lprintf(LO_ERROR, "I_InitMusic: failed to create music temp file %s", music_tmp); + free(music_tmp); music_tmp = NULL; return; + } else + close(fd); + } +#else /* !_WIN32 */ + music_tmp = strdup("doom.tmp"); +#endif + I_AtExit(I_ShutdownMusic, true); + } + return; +#endif + lprintf (LO_INFO, "I_InitMusic: Was compiled without SDL_Mixer support. You should enable experimental music.\n"); +} + +void I_PlaySong(int handle, int looping) +{ + if (use_experimental_music) + { + Exp_PlaySong (handle, looping); + return; + } +#ifdef HAVE_MIXER + if ( music[handle] ) { + //Mix_FadeInMusic(music[handle], looping ? -1 : 0, 500); + Mix_PlayMusic(music[handle], looping ? -1 : 0); + + // haleyjd 10/28/05: make sure volume settings remain consistent + I_SetMusicVolume(snd_MusicVolume); + } +#endif +} + +extern int mus_pause_opt; // From m_misc.c + +void I_PauseSong (int handle) +{ + if (use_experimental_music) + { + Exp_PauseSong (handle); + return; + } +#ifdef HAVE_MIXER + switch(mus_pause_opt) { + case 0: + I_StopSong(handle); + break; + case 1: + switch (Mix_GetMusicType(NULL)) + { + case MUS_NONE: + break; + case MUS_MID: + // SDL_mixer's native MIDI music playing does not pause properly. + // As a workaround, set the volume to 0 when paused. + I_SetMusicVolume(0); + break; + default: + Mix_PauseMusic(); + break; + } + break; + } +#endif + // Default - let music continue +} + +void I_ResumeSong (int handle) +{ + if (use_experimental_music) + { + Exp_ResumeSong (handle); + return; + } +#ifdef HAVE_MIXER + switch(mus_pause_opt) { + case 0: + I_PlaySong(handle,1); + break; + case 1: + switch (Mix_GetMusicType(NULL)) + { + case MUS_NONE: + break; + case MUS_MID: + I_SetMusicVolume(snd_MusicVolume); + break; + default: + Mix_ResumeMusic(); + break; + } + break; + } +#endif + /* Otherwise, music wasn't stopped */ +} + +void I_StopSong(int handle) +{ + if (use_experimental_music) + { + Exp_StopSong (handle); + return; + } +#ifdef HAVE_MIXER + // halt music playback + Mix_HaltMusic(); +#endif +} + +void I_UnRegisterSong(int handle) +{ + if (use_experimental_music) + { + Exp_UnRegisterSong (handle); + return; + } +#ifdef HAVE_MIXER + if ( music[handle] ) { + Mix_FreeMusic(music[handle]); + music[handle] = NULL; + + // Free RWops + if (rw_midi != NULL) + { + //SDL_FreeRW(rw_midi); + rw_midi = NULL; + } + } +#endif +} + +int I_RegisterSong(const void *data, size_t len) +{ + int i; + char *name; + dboolean io_errors = false; + + + if (use_experimental_music) + { + return Exp_RegisterSong (data, len); + } +#ifdef HAVE_MIXER + + if (music_tmp == NULL) + return 0; + + // e6y: new logic by me + // Now you can hear title music in deca.wad + // http://www.doomworld.com/idgames/index.php?id=8808 + // Ability to use mp3 and ogg as inwad lump + + music[0] = NULL; + + if (len > 4 && memcmp(data, "MUS", 3) != 0) + { + // The header has no MUS signature + // Let's try to load this song with SDL + for (i = 0; i < MUSIC_TMP_EXT; i++) + { + // Current SDL_mixer (up to 1.2.8) cannot load some MP3 and OGG + // without proper extension + name = (char*)malloc(strlen(music_tmp) + strlen(music_tmp_ext[i]) + 1); + sprintf(name, "%s%s", music_tmp, music_tmp_ext[i]); + + if (strlen(music_tmp_ext[i]) == 0) + { + //midi + rw_midi = SDL_RWFromConstMem(data, len); + if (rw_midi) + { + music[0] = Mix_LoadMUS_RW(rw_midi, SDL_FALSE); + } + } + + if (!music[0]) + { + io_errors = (M_WriteFile(name, data, len) == 0); + if (!io_errors) + { + music[0] = Mix_LoadMUS(name); + } + } + + free(name); + if (music[0]) + break; // successfully loaded + } + } + + // e6y: from Chocolate-Doom + // Assume a MUS file and try to convert + if (len > 4 && !music[0]) + { + MEMFILE *instream; + MEMFILE *outstream; + void *outbuf; + size_t outbuf_len; + int result; + + instream = mem_fopen_read(data, len); + outstream = mem_fopen_write(); + + // e6y: from chocolate-doom + // New mus -> mid conversion code thanks to Ben Ryves + // This plays back a lot of music closer to Vanilla Doom - eg. tnt.wad map02 + result = mus2mid(instream, outstream); + + if (result != 0) + { + size_t muslen = len; + const unsigned char *musptr = (const unsigned char* )data; + + // haleyjd 04/04/10: scan forward for a MUS header. Evidently DMX was + // capable of doing this, and would skip over any intervening data. That, + // or DMX doesn't use the MUS header at all somehow. + while (musptr < (const unsigned char*)data + len - sizeof(musheader)) + { + // if we found a likely header start, reset the mus pointer to that location, + // otherwise just leave it alone and pray. + if (!strncmp((const char*)musptr, "MUS\x1a", 4)) + { + mem_fclose(instream); + instream = mem_fopen_read(musptr, muslen); + result = mus2mid(instream, outstream); + break; + } + + musptr++; + muslen--; + } + } + + if (result == 0) + { + mem_get_buf(outstream, &outbuf, &outbuf_len); + + rw_midi = SDL_RWFromMem(outbuf, outbuf_len); + if (rw_midi) + { + music[0] = Mix_LoadMUS_RW(rw_midi, SDL_FALSE); + } + + if (!music[0]) + { + io_errors = M_WriteFile(music_tmp, outbuf, outbuf_len) == 0; + + if (!io_errors) + { + // Load the MUS + music[0] = Mix_LoadMUS(music_tmp); + } + } + } + + mem_fclose(instream); + mem_fclose(outstream); + } + + // Failed to load + if (!music[0]) + { + // Conversion failed, free everything + if (rw_midi != NULL) + { + //SDL_FreeRW(rw_midi); + rw_midi = NULL; + } + + if (io_errors) + { + lprintf(LO_ERROR, "Error writing song\n"); + } + else + { + lprintf(LO_ERROR, "Error loading song: %s\n", Mix_GetError()); + } + } + +#endif + return (0); +} + +// cournia - try to load a music file into SDL_Mixer +// returns true if could not load the file +int I_RegisterMusic( const char* filename, musicinfo_t *song ) +{ + if (use_experimental_music) + { + return Exp_RegisterMusic (filename, song); + + } + + +#ifdef HAVE_MIXER + if (!filename) return 1; + if (!song) return 1; + music[0] = Mix_LoadMUS(filename); + if (music[0] == NULL) + { + lprintf(LO_WARN,"Couldn't load music from %s: %s\nAttempting to load default MIDI music.\n", filename, Mix_GetError()); + return 1; + } + else + { + song->data = 0; + song->handle = 0; + song->lumpnum = 0; + return 0; + } +#else + return 1; +#endif +} + +void I_SetMusicVolume(int volume) +{ + if (use_experimental_music) + { + Exp_SetMusicVolume (volume); + return; + } +#ifdef HAVE_MIXER + Mix_VolumeMusic(volume*8); + +#endif +} + + + + + + + +/******************************************************** + +experimental music API + +********************************************************/ + + + +// note that the "handle" passed around by s_sound is ignored +// however, a handle is maintained for the individual music players + +const char *snd_soundfont; // soundfont name for synths that use it +const char *snd_mididev; // midi device to use (portmidiplayer) + +#include "mus2mid.h" + +#include "MUSIC/musicplayer.h" + +#include "MUSIC/oplplayer.h" +#include "MUSIC/madplayer.h" +#include "MUSIC/dumbplayer.h" +#include "MUSIC/flplayer.h" +#include "MUSIC/vorbisplayer.h" +#include "MUSIC/alsaplayer.h" +#include "MUSIC/portmidiplayer.h" + +// list of possible music players +static const music_player_t *music_players[] = +{ // until some ui work is done, the order these appear is the autodetect order. + // of particular importance: things that play mus have to be last, because + // mus2midi very often succeeds even on garbage input + &vorb_player, // vorbisplayer.h + &mp_player, // madplayer.h + &db_player, // dumbplayer.h + &fl_player, // flplayer.h + &opl_synth_player, // oplplayer.h + &pm_player, // portmidiplayer.h + &alsa_player, // alsaplayer.h + NULL +}; +#define NUM_MUS_PLAYERS ((int)(sizeof (music_players) / sizeof (music_player_t *) - 1)) + + +static int music_player_was_init[NUM_MUS_PLAYERS]; + +#define PLAYER_VORBIS "vorbis player" +#define PLAYER_MAD "mad mp3 player" +#define PLAYER_DUMB "dumb tracker player" +#define PLAYER_FLUIDSYNTH "fluidsynth midi player" +#define PLAYER_OPL2 "opl2 synth player" +#define PLAYER_PORTMIDI "portmidi midi player" +#define PLAYER_ALSA "alsa midi player" + +// order in which players are to be tried +char music_player_order[NUM_MUS_PLAYERS][200] = +{ + PLAYER_VORBIS, + PLAYER_MAD, + PLAYER_DUMB, + PLAYER_FLUIDSYNTH, + PLAYER_OPL2, + PLAYER_PORTMIDI, + PLAYER_ALSA, +}; + +// prefered MIDI device +const char *snd_midiplayer; + +const char *midiplayers[midi_player_last + 1] = { + "sdl", "fluidsynth", "opl2", "portmidi", "alsa", NULL}; + +static int current_player = -1; +static const void *music_handle = NULL; + +// songs played directly from wad (no mus->mid conversion) +// won't have this +static void *song_data = NULL; + +int mus_fluidsynth_chorus; +int mus_fluidsynth_reverb; +int mus_fluidsynth_gain; // NSM fine tune fluidsynth output level +int mus_opl_gain; // NSM fine tune OPL output level +const char *mus_portmidi_reset_type; // portmidi reset type +int mus_portmidi_reset_delay; // portmidi delay after reset +int mus_portmidi_filter_sysex; // portmidi block sysex from midi files +int mus_portmidi_reverb_level; // portmidi reverb send level +int mus_portmidi_chorus_level; // portmidi chorus send level + + +static void Exp_ShutdownMusic(void) +{ + int i; + S_StopMusic (); + + for (i = 0; music_players[i]; i++) + { + if (music_player_was_init[i]) + music_players[i]->shutdown (); + } + + if (musmutex) + { + SDL_DestroyMutex (musmutex); + musmutex = NULL; + } +} + + +static void Exp_InitMusic(void) +{ + int i; + musmutex = SDL_CreateMutex (); + + + // todo not so greedy + for (i = 0; music_players[i]; i++) + music_player_was_init[i] = music_players[i]->init (snd_samplerate); + I_AtExit(Exp_ShutdownMusic, true); +} + +static void Exp_PlaySong(int handle, int looping) +{ + if (music_handle) + { + SDL_LockMutex (musmutex); + music_players[current_player]->play (music_handle, looping); + music_players[current_player]->setvolume (snd_MusicVolume); + SDL_UnlockMutex (musmutex); + } + +} + +extern int mus_pause_opt; // From m_misc.c + +static void Exp_PauseSong (int handle) +{ + if (!music_handle) + return; + + SDL_LockMutex (musmutex); + switch (mus_pause_opt) + { + case 0: + music_players[current_player]->stop (); + break; + case 1: + music_players[current_player]->pause (); + break; + default: // Default - let music continue + break; + } + SDL_UnlockMutex (musmutex); +} + +static void Exp_ResumeSong (int handle) +{ + if (!music_handle) + return; + + SDL_LockMutex (musmutex); + switch (mus_pause_opt) + { + case 0: // i'm not sure why we can guarantee looping=true here, + // but that's what the old code did + music_players[current_player]->play (music_handle, 1); + break; + case 1: + music_players[current_player]->resume (); + break; + default: // Default - music was never stopped + break; + } + SDL_UnlockMutex (musmutex); +} + +static void Exp_StopSong(int handle) +{ + if (music_handle) + { + SDL_LockMutex (musmutex); + music_players[current_player]->stop (); + SDL_UnlockMutex (musmutex); + } +} + +static void Exp_UnRegisterSong(int handle) +{ + if (music_handle) + { + SDL_LockMutex (musmutex); + music_players[current_player]->unregistersong (music_handle); + music_handle = NULL; + if (song_data) + { + free (song_data); + song_data = NULL; + } + SDL_UnlockMutex (musmutex); + } +} + +static void Exp_SetMusicVolume (int volume) +{ + if (music_handle) + { + SDL_LockMutex (musmutex); + music_players[current_player]->setvolume (volume); + SDL_UnlockMutex (musmutex); + } +} + +// returns 1 on success, 0 on failure +static int Exp_RegisterSongEx (const void *data, size_t len, int try_mus2mid) +{ + int i, j; + dboolean io_errors = false; + + MEMFILE *instream; + MEMFILE *outstream; + void *outbuf; + size_t outbuf_len; + int result; + + //try_mus2mid = 0; // debug: supress mus2mid conversion completely + + + if (music_handle) + Exp_UnRegisterSong (0); + + + // e6y: new logic by me + // Now you can hear title music in deca.wad + // http://www.doomworld.com/idgames/index.php?id=8808 + // Ability to use mp3 and ogg as inwad lump + + if (len > 4 && memcmp(data, "MUS", 3) != 0) + { + // The header has no MUS signature + // Let's try to load this song directly + + // go through music players in order + int found = 0; + + for (j = 0; j < NUM_MUS_PLAYERS; j++) + { + found = 0; + for (i = 0; music_players[i]; i++) + { + if (strcmp (music_players[i]->name (), music_player_order[j]) == 0) + { + found = 1; + if (music_player_was_init[i]) + { + const void *temp_handle = music_players[i]->registersong (data, len); + if (temp_handle) + { + SDL_LockMutex (musmutex); + current_player = i; + music_handle = temp_handle; + SDL_UnlockMutex (musmutex); + lprintf (LO_INFO, "Exp_RegisterSongEx: Using player %s\n", music_players[i]->name ()); + return 1; + } + } + else + lprintf (LO_INFO, "Exp_RegisterSongEx: Music player %s on preferred list but it failed to init\n", music_players[i]-> name ()); + } + } + if (!found) + lprintf (LO_INFO, "Exp_RegisterSongEx: Couldn't find preferred music player %s in list\n (typo or support not included at compile time)\n", music_player_order[j]); + } + // load failed + } + + + + + // load failed? try mus2mid + if (len > 4 && try_mus2mid) + { + + instream = mem_fopen_read (data, len); + outstream = mem_fopen_write (); + + // e6y: from chocolate-doom + // New mus -> mid conversion code thanks to Ben Ryves + // This plays back a lot of music closer to Vanilla Doom - eg. tnt.wad map02 + result = mus2mid(instream, outstream); + if (result != 0) + { + size_t muslen = len; + const unsigned char *musptr = (const unsigned char*)data; + + // haleyjd 04/04/10: scan forward for a MUS header. Evidently DMX was + // capable of doing this, and would skip over any intervening data. That, + // or DMX doesn't use the MUS header at all somehow. + while (musptr < (const unsigned char*)data + len - sizeof(musheader)) + { + // if we found a likely header start, reset the mus pointer to that location, + // otherwise just leave it alone and pray. + if (!strncmp ((const char*) musptr, "MUS\x1a", 4)) + { + mem_fclose (instream); + instream = mem_fopen_read (musptr, muslen); + result = mus2mid (instream, outstream); + break; + } + + musptr++; + muslen--; + } + } + if (result == 0) + { + mem_get_buf(outstream, &outbuf, &outbuf_len); + + // recopy so we can free the MEMFILE + song_data = malloc (outbuf_len); + if (song_data) + memcpy (song_data, outbuf, outbuf_len); + + mem_fclose(instream); + mem_fclose(outstream); + + if (song_data) + { + return Exp_RegisterSongEx (song_data, outbuf_len, 0); + } + } + } + + lprintf (LO_ERROR, "Exp_RegisterSongEx: Failed\n"); + return 0; +} + + +static int Exp_RegisterSong (const void *data, size_t len) +{ + Exp_RegisterSongEx (data, len, 1); + return 0; +} + +// try register external music file (not in WAD) + +static int Exp_RegisterMusic (const char *filename, musicinfo_t *song) +{ + int len; + + len = M_ReadFile (filename, (byte **) &song_data); + + if (len == -1) + { + lprintf (LO_WARN, "Couldn't read %s\nAttempting to load default MIDI music.\n", filename); + return 1; + } + + if (!Exp_RegisterSongEx (song_data, len, 1)) + { + free (song_data); + song_data = NULL; + lprintf(LO_WARN, "Couldn't load music from %s\nAttempting to load default MIDI music.\n", filename); + return 1; // failure + } + + song->data = 0; + song->handle = 0; + song->lumpnum = 0; + return 0; +} + +static void Exp_UpdateMusic (void *buff, unsigned nsamp) +{ + + if (!music_handle) + { + memset (buff, 0, nsamp * 4); + return; + } + + + music_players[current_player]->render (buff, nsamp); +} + +void M_ChangeMIDIPlayer(void) +{ + int experimental_music; + +#ifdef HAVE_OWN_MUSIC + // do not bother about small memory leak + snd_midiplayer = strdup(midiplayers[midi_player_sdl]); + use_experimental_music = 0; + return; +#endif + + if (!strcasecmp(snd_midiplayer, midiplayers[midi_player_sdl])) + { + experimental_music = false; + } + else + { + experimental_music = true; + + if (!strcasecmp(snd_midiplayer, midiplayers[midi_player_fluidsynth])) + { + strcpy(music_player_order[3], PLAYER_FLUIDSYNTH); + strcpy(music_player_order[4], PLAYER_OPL2); + strcpy(music_player_order[6], PLAYER_PORTMIDI); + strcpy(music_player_order[5], PLAYER_ALSA); + } + else if (!strcasecmp(snd_midiplayer, midiplayers[midi_player_opl2])) + { + strcpy(music_player_order[3], PLAYER_OPL2); + strcpy(music_player_order[6], PLAYER_PORTMIDI); + strcpy(music_player_order[4], PLAYER_ALSA); + strcpy(music_player_order[5], PLAYER_FLUIDSYNTH); + } + else if (!strcasecmp(snd_midiplayer, midiplayers[midi_player_alsa])) + { + strcpy(music_player_order[3], PLAYER_ALSA); + strcpy(music_player_order[5], PLAYER_FLUIDSYNTH); + strcpy(music_player_order[6], PLAYER_OPL2); + strcpy(music_player_order[4], PLAYER_PORTMIDI); + } + else if (!strcasecmp(snd_midiplayer, midiplayers[midi_player_portmidi])) + { + strcpy(music_player_order[3], PLAYER_PORTMIDI); + strcpy(music_player_order[6], PLAYER_ALSA); + strcpy(music_player_order[4], PLAYER_FLUIDSYNTH); + strcpy(music_player_order[5], PLAYER_OPL2); + } + } + +#if 1 + if (use_experimental_music == -1) + { + use_experimental_music = experimental_music; + } + else + { + if (experimental_music && use_experimental_music) + { + S_StopMusic(); + S_RestartMusic(); + } + } +#else + S_StopMusic(); + + if (use_experimental_music != experimental_music) + { + I_ShutdownMusic(); + + S_Stop(); + I_ShutdownSound(); + + use_experimental_music = experimental_music; + + I_InitSound(); + } + + S_RestartMusic(); +#endif +} + +#endif diff --git a/src/SDL/i_sshot.c b/src/SDL/i_sshot.c new file mode 100644 index 0000000..b3a2529 --- /dev/null +++ b/src/SDL/i_sshot.c @@ -0,0 +1,135 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2006 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Screenshot functions, moved out of i_video.c + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "SDL.h" + +#ifdef HAVE_LIBSDL2_IMAGE +#include +#endif + +#include "doomstat.h" +#include "doomdef.h" +#include "doomtype.h" +#include "v_video.h" +#include "i_video.h" +#include "z_zone.h" +#include "lprintf.h" + +int renderW; +int renderH; + +void I_UpdateRenderSize(void) +{ + if (V_GetMode() == VID_MODEGL) + { + renderW = SCREENWIDTH; + renderH = SCREENHEIGHT; + } + else + { + SDL_GetRendererOutputSize(sdl_renderer, &renderW, &renderH); + } +} + +// +// I_ScreenShot // Modified to work with SDL2 resizeable window and fullscreen desktop - DTIED +// + +int I_ScreenShot(const char *fname) +{ + int result = -1; + unsigned char *pixels = I_GrabScreen(); + SDL_Surface *screenshot = NULL; + + if (pixels) + { + screenshot = SDL_CreateRGBSurfaceFrom(pixels, renderW, renderH, 24, + renderW * 3, 0x000000ff, 0x0000ff00, 0x00ff0000, 0); + } + + if (screenshot) + { +#ifdef HAVE_LIBSDL2_IMAGE + result = IMG_SavePNG(screenshot, fname); +#else + result = SDL_SaveBMP(screenshot, fname); +#endif + SDL_FreeSurface(screenshot); + } + return result; +} + +// NSM +// returns current screen contents as RGB24 (raw) +// returned pointer should be freed when done +// +// Modified to work with SDL2 resizeable window and fullscreen desktop - DTIED +// + +unsigned char *I_GrabScreen(void) +{ + static unsigned char *pixels = NULL; + static int pixels_size = 0; + int size; + + I_UpdateRenderSize(); + + #ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + return gld_ReadScreen(); + } + #endif + + size = renderW * renderH * 3; + if (!pixels || size > pixels_size) + { + pixels_size = size; + pixels = (unsigned char*)realloc(pixels, size); + } + + if (pixels && size) + { + SDL_Rect screen = { 0, 0, renderW, renderH }; + SDL_RenderReadPixels(sdl_renderer, &screen, SDL_PIXELFORMAT_RGB24, pixels, renderW * 3); + } + + return pixels; +} diff --git a/src/SDL/i_system.c b/src/SDL/i_system.c new file mode 100644 index 0000000..2094647 --- /dev/null +++ b/src/SDL/i_system.c @@ -0,0 +1,557 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Misc system stuff needed by Doom, implemented for Linux. + * Mainly timer handling, and ENDOOM/ENDBOOM. + * + *----------------------------------------------------------------------------- + */ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#ifdef _MSC_VER +#define F_OK 0 /* Check for file existence */ +#define W_OK 2 /* Check for write permission */ +#define R_OK 4 /* Check for read permission */ +#include +#include +#else +#include +#endif +#include + +#include "SDL.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef _MSC_VER +#include +#endif +#include +#include +#include + +#ifndef PRBOOM_SERVER +#include "m_argv.h" +#endif +#include "lprintf.h" +#include "doomtype.h" +#include "doomdef.h" +#ifndef PRBOOM_SERVER +#include "d_player.h" +#include "m_fixed.h" +#include "r_fps.h" +#include "e6y.h" +#endif +#include "i_system.h" + +#ifdef __GNUG__ +#pragma implementation "i_system.h" +#endif +#include "i_system.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "z_zone.h" + +#include "m_io.h" + +void I_uSleep(unsigned long usecs) +{ + SDL_Delay(usecs/1000); +} + +#ifndef PRBOOM_SERVER +static dboolean InDisplay = false; +static int saved_gametic = -1; +dboolean realframe = false; + +dboolean I_StartDisplay(void) +{ + if (InDisplay) + return false; + + realframe = (!movement_smooth) || (gametic > saved_gametic); + + if (realframe) + saved_gametic = gametic; + + InDisplay = true; + return true; +} + +void I_EndDisplay(void) +{ + InDisplay = false; +} + +fixed_t I_GetTimeFrac (void) +{ + fixed_t frac; + + if (!movement_smooth) + { + frac = FRACUNIT; + } + else + { + frac = I_TickElapsedTime(); + } + + return frac; +} +#endif + +/* + * I_GetRandomTimeSeed + * + * CPhipps - extracted from G_ReloadDefaults because it is O/S based + */ +unsigned long I_GetRandomTimeSeed(void) +{ + return (unsigned long)time(NULL); +} + +/* cphipps - I_GetVersionString + * Returns a version string in the given buffer + */ +const char* I_GetVersionString(char* buf, size_t sz) +{ + snprintf(buf,sz,"%s v%s (%s)",PACKAGE_NAME,PACKAGE_VERSION,PACKAGE_HOMEPAGE); + return buf; +} + +/* cphipps - I_SigString + * Returns a string describing a signal number + */ +const char* I_SigString(char* buf, size_t sz, int signum) +{ +#ifdef HAVE_STRSIGNAL + if (strsignal(signum) && strlen(strsignal(signum)) < sz) + strcpy(buf,strsignal(signum)); + else +#endif + snprintf(buf,sz,"signal %d",signum); + return buf; +} + +#ifndef PRBOOM_SERVER +dboolean I_FileToBuffer(const char *filename, byte **data, int *size) +{ + FILE *hfile; + + dboolean result = false; + byte *buffer = NULL; + size_t filesize = 0; + + hfile = M_fopen(filename, "rb"); + if (hfile) + { + fseek(hfile, 0, SEEK_END); + filesize = ftell(hfile); + fseek(hfile, 0, SEEK_SET); + + buffer = (byte*)malloc(filesize); + if (buffer) + { + if (fread(buffer, filesize, 1, hfile) == 1) + { + result = true; + + if (data) + { + *data = buffer; + } + + if (size) + { + *size = filesize; + } + } + } + + fclose(hfile); + } + + if (!result) + { + free(buffer); + buffer = NULL; + } + + return result; +} +#endif // PRBOOM_SERVER + +/* + * I_Read + * + * cph 2001/11/18 - wrapper for read(2) which handles partial reads and aborts + * on error. + */ +void I_Read(int fd, void* vbuf, size_t sz) +{ + unsigned char* buf = (unsigned char*)vbuf; + + while (sz) { + int rc = read(fd,buf,sz); + if (rc <= 0) { + I_Error("I_Read: read failed: %s", rc ? strerror(errno) : "EOF"); + } + sz -= rc; buf += rc; + } +} + +/* + * I_Filelength + * + * Return length of an open file. + */ + +int I_Filelength(int handle) +{ + struct stat fileinfo; + if (fstat(handle,&fileinfo) == -1) + I_Error("I_Filelength: %s",strerror(errno)); + return fileinfo.st_size; +} + +#ifndef PRBOOM_SERVER + +// Return the path where the executable lies -- Lee Killough +// proff_fs 2002-07-04 - moved to i_system +#ifdef _WIN32 + +void I_SwitchToWindow(HWND hwnd) +{ + typedef BOOL (WINAPI *TSwitchToThisWindow) (HWND wnd, BOOL restore); + static TSwitchToThisWindow SwitchToThisWindow = NULL; + + if (!SwitchToThisWindow) + SwitchToThisWindow = (TSwitchToThisWindow)GetProcAddress(GetModuleHandle("user32.dll"), "SwitchToThisWindow"); + + if (SwitchToThisWindow) + { + HWND hwndLastActive = GetLastActivePopup(hwnd); + + if (IsWindowVisible(hwndLastActive)) + hwnd = hwndLastActive; + + SetForegroundWindow(hwnd); + Sleep(100); + SwitchToThisWindow(hwnd, TRUE); + } +} + +const char *I_DoomExeDir(void) +{ + static const char current_dir_dummy[] = {"."}; // proff - rem extra slash 8/21/03 + static char *base; + if (!base) // cache multiple requests + { + size_t len = strlen(*myargv); + char *p = (base = (char*)malloc(len+1)) + len - 1; + strcpy(base,*myargv); + while (p > base && *p!='/' && *p!='\\') + *p--=0; + if (*p=='/' || *p=='\\') + *p--=0; + if (strlen(base)<2 || M_access(base, W_OK) != 0) + { + free(base); + base = (char*)malloc(1024); + if (!M_getcwd(base,1024) || M_access(base, W_OK) != 0) + strcpy(base, current_dir_dummy); + } + } + return base; +} + +const char* I_GetTempDir(void) +{ + static char tmp_path[PATH_MAX] = {0}; + + if (tmp_path[0] == 0) + { + GetTempPath(sizeof(tmp_path), tmp_path); + } + + return tmp_path; +} + +#elif defined(AMIGA) + +const char *I_DoomExeDir(void) +{ + return "PROGDIR:"; +} + +const char* I_GetTempDir(void) +{ + return "PROGDIR:"; +} + +#elif defined(MACOSX) + +/* Defined elsewhere */ + +#else +// cph - V.Aguilar (5/30/99) suggested return ~/.lxdoom/, creating +// if non-existant +// cph 2006/07/23 - give prboom+ its own dir +static const char prboom_dir[] = {"prboom-plus"}; + +const char *I_DoomExeDir(void) +{ + static char *base; + struct stat data_dir; + + if (!base) // cache multiple requests + { + char *home = M_getenv("HOME"); + char *p_home = strdup(home); + size_t len = strlen(home); + size_t p_len = (len + strlen(prboom_dir) + 3); + + // I've had trouble with trailing slashes before... + if (p_home[len-1] == '/') p_home[len-1] = 0; + + base = malloc(p_len); + snprintf(base, p_len, "%s/.%s", p_home, prboom_dir); + free(p_home); + + // if ~/.$prboom_dir doesn't exist, + // create and use directory in XDG_DATA_HOME + if (M_stat(base, &data_dir) || !S_ISDIR(data_dir.st_mode)) + { + // SDL creates this directory if it doesn't exist + char *prefpath = SDL_GetPrefPath("", prboom_dir); + size_t prefsize = strlen(prefpath); + + free(base); + base = strdup(prefpath); + // SDL_GetPrefPath always returns with trailing slash + if (base[prefsize-1] == '/') base[prefsize-1] = 0; + SDL_free(prefpath); + } +// mkdir(base, S_IRUSR | S_IWUSR | S_IXUSR); + } + return base; +} + +const char *I_GetTempDir(void) +{ + return "/tmp"; +} + +#endif + +/* + * HasTrailingSlash + * + * cphipps - simple test for trailing slash on dir names + */ + +dboolean HasTrailingSlash(const char* dn) +{ + return ( (dn[strlen(dn)-1] == '/') +#if defined(_WIN32) + || (dn[strlen(dn)-1] == '\\') +#endif +#if defined(AMIGA) + || (dn[strlen(dn)-1] == ':') +#endif + ); +} + +/* + * I_FindFile + * + * proff_fs 2002-07-04 - moved to i_system + * + * cphipps 19/1999 - writen to unify the logic in FindIWADFile and the WAD + * autoloading code. + * Searches the standard dirs for a named WAD file + * The dirs are listed at the start of the function + */ + +#ifndef MACOSX /* OSX defines its search paths elsewhere. */ + +#ifdef _WIN32 +#define PATH_SEPARATOR ';' +#else +#define PATH_SEPARATOR ':' +#endif + +char* I_FindFileInternal(const char* wfname, const char* ext, dboolean isStatic) +{ + // lookup table of directories to search + static struct { + const char *dir; // directory + const char *sub; // subdirectory + const char *env; // environment variable + const char *(*func)(void); // for I_DoomExeDir + } search0[] = { + {NULL, NULL, NULL, I_DoomExeDir}, // config directory + {NULL}, // current working directory + {PRBOOMDATADIR}, // supplemental data directory + {NULL, NULL, "DOOMWADDIR"}, // run-time $DOOMWADDIR + {DOOMWADDIR}, // build-time configured DOOMWADDIR + {NULL, "doom", "HOME"}, // ~/doom + {NULL, NULL, "HOME"}, // ~ + {"/usr/local/share/games/doom"}, + {"/usr/share/games/doom"}, + {"/usr/local/share/doom"}, + {"/usr/share/doom"}, + }, *search; + + static size_t num_search; + size_t i; + size_t pl; + + static char static_p[PATH_MAX]; + char * dinamic_p = NULL; + char *p = (isStatic ? static_p : dinamic_p); + + if (!wfname) + return NULL; + + if (!num_search) + { + char *dwp; + + // initialize with the static lookup table + num_search = sizeof(search0)/sizeof(*search0); + search = malloc(num_search * sizeof(*search)); + memcpy(search, search0, num_search * sizeof(*search)); + + // add each directory from the $DOOMWADPATH environment variable + if ((dwp = M_getenv("DOOMWADPATH"))) + { + char *left, *ptr, *dup_dwp; + + dup_dwp = strdup(dwp); + left = dup_dwp; + + for (;;) + { + ptr = strchr(left, PATH_SEPARATOR); + if (ptr != NULL) + { + *ptr = '\0'; + + num_search++; + search = realloc(search, num_search * sizeof(*search)); + memset(&search[num_search-1], 0, sizeof(*search)); + search[num_search-1].dir = strdup(left); + + left = ptr + 1; + } + else + { + break; + } + } + + num_search++; + search = realloc(search, num_search * sizeof(*search)); + memset(&search[num_search-1], 0, sizeof(*search)); + search[num_search-1].dir = strdup(left); + + free(dup_dwp); + } + } + + /* Precalculate a length we will need in the loop */ + pl = strlen(wfname) + (ext ? strlen(ext) : 0) + 4; + + for (i = 0; i < num_search; i++) { + const char * d = NULL; + const char * s = NULL; + /* Each entry in the switch sets d to the directory to look in, + * and optionally s to a subdirectory of d */ + // switch replaced with lookup table + if (search[i].env) { + if (!(d = M_getenv(search[i].env))) + continue; + } else if (search[i].func) + d = search[i].func(); + else + d = search[i].dir; + s = search[i].sub; + + if (!isStatic) + p = (char*)malloc((d ? strlen(d) : 0) + (s ? strlen(s) : 0) + pl); + sprintf(p, "%s%s%s%s%s", d ? d : "", (d && !HasTrailingSlash(d)) ? "/" : "", + s ? s : "", (s && !HasTrailingSlash(s)) ? "/" : "", + wfname); + + if (ext && M_access(p,F_OK)) + strcat(p, ext); + if (!M_access(p,F_OK)) { + if (!isStatic) + lprintf(LO_INFO, " found %s\n", p); + return p; + } + if (!isStatic) + free(p); + } + return NULL; +} + +char* I_FindFile(const char* wfname, const char* ext) +{ + return I_FindFileInternal(wfname, ext, false); +} + +const char* I_FindFile2(const char* wfname, const char* ext) +{ + return (const char*) I_FindFileInternal(wfname, ext, true); +} + +#endif + +#endif // PRBOOM_SERVER diff --git a/src/SDL/i_video.c b/src/SDL/i_video.c new file mode 100644 index 0000000..6cc1afb --- /dev/null +++ b/src/SDL/i_video.c @@ -0,0 +1,1683 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2006 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * DOOM graphics stuff for SDL + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#endif // _WIN32 + +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "SDL.h" +//e6y +#ifdef _WIN32 +#include +#endif + +#include "m_argv.h" +#include "doomstat.h" +#include "doomdef.h" +#include "doomtype.h" +#include "v_video.h" +#include "r_draw.h" +#include "r_things.h" +#include "r_plane.h" +#include "r_main.h" +#include "f_wipe.h" +#include "d_main.h" +#include "d_event.h" +#include "d_deh.h" +#include "i_joy.h" +#include "i_video.h" +#include "i_capture.h" +#include "z_zone.h" +#include "s_sound.h" +#include "sounds.h" +#include "w_wad.h" +#include "st_stuff.h" +#include "am_map.h" +#include "g_game.h" +#include "lprintf.h" +#include "i_system.h" + +#ifdef GL_DOOM +#include "gl_struct.h" +#endif + +#include "e6y.h"//e6y +#include "i_main.h" + +//e6y: new mouse code +static SDL_Cursor* cursors[2] = {NULL, NULL}; + +dboolean window_focused; +static int mouse_currently_grabbed = true; + +// Window resize state. +static void ApplyWindowResize(SDL_Event *resize_event); + +const char *sdl_video_window_pos; + +static void ActivateMouse(void); +static void DeactivateMouse(void); +//static int AccelerateMouse(int val); +static void I_ReadMouse(void); +static dboolean MouseShouldBeGrabbed(); +static void UpdateFocus(void); + +int gl_colorbuffer_bits=16; +int gl_depthbuffer_bits=16; + +extern void M_QuitDOOM(int choice); +int use_fullscreen; +int desired_fullscreen; +int exclusive_fullscreen; +int render_vsync; +int render_screen_multiply; +int integer_scaling; +int vanilla_keymap; +SDL_Surface *screen; +static SDL_Surface *buffer; +SDL_Window *sdl_window; +SDL_Renderer *sdl_renderer; +static SDL_Texture *sdl_texture; +static SDL_GLContext sdl_glcontext; +static unsigned int windowid = 0; +static SDL_Rect src_rect = { 0, 0, 0, 0 }; +static int display_index; +static SDL_DisplayMode desktop_mode = {.w = 16384, .h = 16384}; + +//////////////////////////////////////////////////////////////////////////// +// Input code +static int leds_always_off = 0; // Expected by m_misc, not relevant + +// Mouse handling +extern int usemouse; // config file var +static dboolean mouse_enabled; // usemouse, but can be overriden by -nomouse + +video_mode_t I_GetModeFromString(const char *modestr); + +///////////////////////////////////////////////////////////////////////////////// +// Keyboard handling + +// Vanilla keymap taken from chocolate-doom and adjusted for prboom-plus +#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', \ + KEYD_ENTER, KEYD_ESCAPE, KEYD_BACKSPACE, KEYD_TAB, ' ', /* 40-49 */ \ + KEYD_MINUS, KEYD_EQUALS, '[', ']', '\\', \ + '\\', ';', '\'', '`', ',', /* 50-59 */ \ + '.', '/', KEYD_CAPSLOCK, KEYD_F1, KEYD_F2, \ + KEYD_F3, KEYD_F4, KEYD_F5, KEYD_F6, KEYD_F7, /* 60-69 */ \ + KEYD_F8, KEYD_F9, KEYD_F10, KEYD_F11, KEYD_F12, KEYD_PRINTSC, \ + KEYD_SCROLLLOCK, KEYD_PAUSE, KEYD_INSERT, KEYD_HOME, /* 70-79 */ \ + KEYD_PAGEUP, KEYD_DEL, KEYD_END, KEYD_PAGEDOWN, KEYD_RIGHTARROW, \ + KEYD_LEFTARROW, KEYD_DOWNARROW, KEYD_UPARROW, /* 80-89 */ \ + KEYD_NUMLOCK, KEYD_KEYPADDIVIDE, \ + KEYD_KEYPADMULTIPLY, KEYD_KEYPADMINUS, KEYD_KEYPADPLUS, \ + KEYD_KEYPADENTER, KEYD_KEYPAD1, KEYD_KEYPAD2, KEYD_KEYPAD3, \ + KEYD_KEYPAD4, KEYD_KEYPAD5, KEYD_KEYPAD6, /* 90-99 */ \ + KEYD_KEYPAD7, KEYD_KEYPAD8, KEYD_KEYPAD9, KEYD_KEYPAD0, \ + KEYD_KEYPADPERIOD, 0, 0, 0, KEYD_EQUALS /* 100-103 */ \ +} +// Map keys like vanilla doom +static int VanillaTranslateKey(SDL_Keysym* key) +{ + static const int scancode_map[] = SCANCODE_TO_KEYS_ARRAY ; + int rc = 0, sc = key->scancode; + + if (sc > 3 && sc < sizeof(scancode_map)/sizeof(scancode_map[0])) + rc = scancode_map[sc]; + // Key is mapped.. + if (rc) return rc; + + switch (sc) { // Code (Ctrl/Shift/Alt) from scancode. + case SDL_SCANCODE_LSHIFT: + case SDL_SCANCODE_RSHIFT: return KEYD_RSHIFT; + case SDL_SCANCODE_LCTRL: + case SDL_SCANCODE_RCTRL: return KEYD_RCTRL; + case SDL_SCANCODE_LALT: + case SDL_SCANCODE_RALT: + case SDL_SCANCODE_LGUI: + case SDL_SCANCODE_RGUI: return KEYD_RALT; + + // Default to the symbolic key (outside of vanilla keys) + default: return key->sym; + } +} + +// +// Translates the key currently in key +// + +static int I_TranslateKey(SDL_Keysym* key) +{ + int rc = 0; + + if (vanilla_keymap) + return VanillaTranslateKey(key); + + switch (key->sym) { + case SDLK_LEFT: rc = KEYD_LEFTARROW; break; + case SDLK_RIGHT: rc = KEYD_RIGHTARROW; break; + case SDLK_DOWN: rc = KEYD_DOWNARROW; break; + case SDLK_UP: rc = KEYD_UPARROW; break; + case SDLK_ESCAPE: rc = KEYD_ESCAPE; break; + case SDLK_RETURN: rc = KEYD_ENTER; break; + case SDLK_TAB: rc = KEYD_TAB; break; + case SDLK_F1: rc = KEYD_F1; break; + case SDLK_F2: rc = KEYD_F2; break; + case SDLK_F3: rc = KEYD_F3; break; + case SDLK_F4: rc = KEYD_F4; break; + case SDLK_F5: rc = KEYD_F5; break; + case SDLK_F6: rc = KEYD_F6; break; + case SDLK_F7: rc = KEYD_F7; break; + case SDLK_F8: rc = KEYD_F8; break; + case SDLK_F9: rc = KEYD_F9; break; + case SDLK_F10: rc = KEYD_F10; break; + case SDLK_F11: rc = KEYD_F11; break; + case SDLK_F12: rc = KEYD_F12; break; + case SDLK_BACKSPACE: rc = KEYD_BACKSPACE; break; + case SDLK_DELETE: rc = KEYD_DEL; break; + case SDLK_INSERT: rc = KEYD_INSERT; break; + case SDLK_PAGEUP: rc = KEYD_PAGEUP; break; + case SDLK_PAGEDOWN: rc = KEYD_PAGEDOWN; break; + case SDLK_HOME: rc = KEYD_HOME; break; + case SDLK_END: rc = KEYD_END; break; + case SDLK_PAUSE: rc = KEYD_PAUSE; break; + case SDLK_EQUALS: rc = KEYD_EQUALS; break; + case SDLK_MINUS: rc = KEYD_MINUS; break; + case SDLK_KP_0: rc = KEYD_KEYPAD0; break; + case SDLK_KP_1: rc = KEYD_KEYPAD1; break; + case SDLK_KP_2: rc = KEYD_KEYPAD2; break; + case SDLK_KP_3: rc = KEYD_KEYPAD3; break; + case SDLK_KP_4: rc = KEYD_KEYPAD4; break; + case SDLK_KP_5: rc = KEYD_KEYPAD5; break; + case SDLK_KP_6: rc = KEYD_KEYPAD6; break; + case SDLK_KP_7: rc = KEYD_KEYPAD7; break; + case SDLK_KP_8: rc = KEYD_KEYPAD8; break; + case SDLK_KP_9: rc = KEYD_KEYPAD9; break; + case SDLK_KP_PLUS: rc = KEYD_KEYPADPLUS; break; + case SDLK_KP_MINUS: rc = KEYD_KEYPADMINUS; break; + case SDLK_KP_DIVIDE: rc = KEYD_KEYPADDIVIDE; break; + case SDLK_KP_MULTIPLY: rc = KEYD_KEYPADMULTIPLY; break; + case SDLK_KP_ENTER: rc = KEYD_KEYPADENTER; break; + case SDLK_KP_PERIOD: rc = KEYD_KEYPADPERIOD; break; + case SDLK_LSHIFT: + case SDLK_RSHIFT: rc = KEYD_RSHIFT; break; + case SDLK_LCTRL: + case SDLK_RCTRL: rc = KEYD_RCTRL; break; + case SDLK_LALT: + case SDLK_LGUI: + case SDLK_RALT: + case SDLK_RGUI: rc = KEYD_RALT; break; + case SDLK_CAPSLOCK: rc = KEYD_CAPSLOCK; break; + case SDLK_PRINTSCREEN: rc = KEYD_PRINTSC; break; + case SDLK_SCROLLLOCK: rc = KEYD_SCROLLLOCK; break; + default: rc = key->sym; break; + } + + return rc; + +} + +///////////////////////////////////////////////////////////////////////////////// +// Main input code + +/* cph - pulled out common button code logic */ +//e6y static +static int I_SDLtoDoomMouseState(Uint32 buttonstate) +{ + return 0 + | (buttonstate & SDL_BUTTON(1) ? 1 : 0) + | (buttonstate & SDL_BUTTON(2) ? 2 : 0) + | (buttonstate & SDL_BUTTON(3) ? 4 : 0) + | (buttonstate & SDL_BUTTON(6) ? 8 : 0) + | (buttonstate & SDL_BUTTON(7) ? 16 : 0) + | (buttonstate & SDL_BUTTON(4) ? 32 : 0) + | (buttonstate & SDL_BUTTON(5) ? 64 : 0) + | (buttonstate & SDL_BUTTON(8) ? 128 : 0) + ; +} + +static void I_GetEvent(void) +{ + event_t event; + + SDL_Event SDLEvent; + SDL_Event *Event = &SDLEvent; + + static int mwheeluptic = 0, mwheeldowntic = 0; + +while (SDL_PollEvent(Event)) +{ + switch (Event->type) { + case SDL_KEYDOWN: +#ifdef MACOSX + if (Event->key.keysym.mod & KMOD_META) + { + // Switch windowed<->fullscreen if pressed + if (Event->key.keysym.sym == SDLK_f) + { + V_ToggleFullscreen(); + break; + } + } +#else + if (Event->key.keysym.mod & KMOD_LALT) + { + // Prevent executing action on Alt-Tab + if (Event->key.keysym.sym == SDLK_TAB) + { + break; + } + // Switch windowed<->fullscreen if pressed Alt-Enter + else if (Event->key.keysym.sym == SDLK_RETURN) + { + V_ToggleFullscreen(); + break; + } + // Immediately exit on Alt+F4 ("Boss Key") + else if (Event->key.keysym.sym == SDLK_F4) + { + I_SafeExit(0); + break; + } + } +#endif + event.type = ev_keydown; + event.data1 = I_TranslateKey(&Event->key.keysym); + D_PostEvent(&event); + break; + + case SDL_KEYUP: + { + event.type = ev_keyup; + event.data1 = I_TranslateKey(&Event->key.keysym); + D_PostEvent(&event); + } + break; + + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + if (mouse_enabled && window_focused) + { + event.type = ev_mouse; + event.data1 = I_SDLtoDoomMouseState(SDL_GetMouseState(NULL, NULL)); + event.data2 = event.data3 = 0; + D_PostEvent(&event); + } + break; + + case SDL_MOUSEWHEEL: + if (mouse_enabled && window_focused) + { + if (Event->wheel.y > 0) + { + event.type = ev_keydown; + event.data1 = KEYD_MWHEELUP; + mwheeluptic = gametic; + D_PostEvent(&event); + } + else if (Event->wheel.y < 0) + { + event.type = ev_keydown; + event.data1 = KEYD_MWHEELDOWN; + mwheeldowntic = gametic; + D_PostEvent(&event); + } + } + break; + + case SDL_WINDOWEVENT: + if (Event->window.windowID == windowid) + { + switch (Event->window.event) + { + case SDL_WINDOWEVENT_FOCUS_GAINED: + case SDL_WINDOWEVENT_FOCUS_LOST: + UpdateFocus(); + break; + case SDL_WINDOWEVENT_SIZE_CHANGED: + ApplyWindowResize(Event); + break; + } + } + break; + + case SDL_QUIT: + S_StartSound(NULL, sfx_swtchn); + M_QuitDOOM(0); + + default: + break; + } +} + + if(mwheeluptic && mwheeluptic + 1 < gametic) + { + event.type = ev_keyup; + event.data1 = KEYD_MWHEELUP; + D_PostEvent(&event); + mwheeluptic = 0; + } + + if(mwheeldowntic && mwheeldowntic + 1 < gametic) + { + event.type = ev_keyup; + event.data1 = KEYD_MWHEELDOWN; + D_PostEvent(&event); + mwheeldowntic = 0; + } +} + +// +// I_StartTic +// + +void I_StartTic (void) +{ + I_GetEvent(); + + I_ReadMouse(); + + I_PollJoystick(); +} + +// +// I_StartFrame +// +void I_StartFrame (void) +{ +} + +// +// I_InitInputs +// + +static void I_InitInputs(void) +{ + static Uint8 empty_cursor_data = 0; + + int nomouse_parm = M_CheckParm("-nomouse"); + + // check if the user wants to use the mouse + mouse_enabled = usemouse && !nomouse_parm; + + SDL_PumpEvents(); + + // Save the default cursor so it can be recalled later + cursors[0] = SDL_GetCursor(); + // Create an empty cursor + cursors[1] = SDL_CreateCursor(&empty_cursor_data, &empty_cursor_data, 8, 1, 0, 0); + + if (mouse_enabled) + { + MouseAccelChanging(); + } + + I_InitJoystick(); +} +///////////////////////////////////////////////////////////////////////////// + +// I_SkipFrame +// +// Returns true if it thinks we can afford to skip this frame + +inline static dboolean I_SkipFrame(void) +{ + static int frameno; + + frameno++; + switch (gamestate) { + case GS_LEVEL: + if (!paused) + return false; + default: + // Skip odd frames + return (frameno & 1) ? true : false; + } +} + +/////////////////////////////////////////////////////////// +// Palette stuff. +// +static void I_UploadNewPalette(int pal, int force) +{ + // This is used to replace the current 256 colour cmap with a new one + // Used by 256 colour PseudoColor modes + + // Array of SDL_Color structs used for setting the 256-colour palette + static SDL_Color* colours; + static int cachedgamma; + static size_t num_pals; + + if (V_GetMode() == VID_MODEGL) + return; + + if ((colours == NULL) || (cachedgamma != usegamma) || force) { + int pplump = W_GetNumForName("PLAYPAL"); + int gtlump = (W_CheckNumForName)("GAMMATBL",ns_prboom); + register const byte * palette = (const byte*)W_CacheLumpNum(pplump); + register const byte * const gtable = (const byte *)W_CacheLumpNum(gtlump) + 256*(cachedgamma = usegamma); + register int i; + + num_pals = W_LumpLength(pplump) / (3*256); + num_pals *= 256; + + if (!colours) { + // First call - allocate and prepare colour array + colours = (SDL_Color*)malloc(sizeof(*colours)*num_pals); + } + + // set the colormap entries + for (i=0 ; (size_t)i= num_pals) + I_Error("I_UploadNewPalette: Palette number out of range (%d>=%d)", + pal, num_pals); +#endif + + SDL_SetPaletteColors(screen->format->palette, colours+256*pal, 0, 256); +} + +////////////////////////////////////////////////////////////////////////////// +// Graphics API + +void I_ShutdownGraphics(void) +{ + SDL_FreeCursor(cursors[1]); + DeactivateMouse(); +} + +// +// I_UpdateNoBlit +// +void I_UpdateNoBlit (void) +{ +} + +// +// I_FinishUpdate +// +static int newpal = 0; +#define NO_PALETTE_CHANGE 1000 + +void I_FinishUpdate (void) +{ + //e6y: new mouse code + UpdateGrab(); + + // The screen wipe following pressing the exit switch on a level + // is noticably jerkier with I_SkipFrame + // if (I_SkipFrame())return; + +#ifdef MONITOR_VISIBILITY + //!!if (!(SDL_GetAppState()&SDL_APPACTIVE)) { + //!! return; + //!!} +#endif + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) { + // proff 04/05/2000: swap OpenGL buffers + gld_Finish(); + return; + } +#endif + + if (SDL_MUSTLOCK(screen)) { + int h; + byte *src; + byte *dest; + + if (SDL_LockSurface(screen) < 0) { + lprintf(LO_INFO,"I_FinishUpdate: %s\n", SDL_GetError()); + return; + } + + dest=(byte*)screen->pixels; + src=screens[0].data; + h=screen->h; + for (; h>0; h--) + { + memcpy(dest,src,SCREENWIDTH*V_GetPixelDepth()); //e6y + dest+=screen->pitch; + src+=screens[0].byte_pitch; + } + + SDL_UnlockSurface(screen); + } + + /* Update the display buffer (flipping video pages if supported) + * If we need to change palette, that implicitely does a flip */ + if (newpal != NO_PALETTE_CHANGE) { + I_UploadNewPalette(newpal, false); + newpal = NO_PALETTE_CHANGE; + } + + // Blit from the paletted 8-bit screen buffer to the intermediate + // 32-bit RGBA buffer that we can load into the texture. + SDL_LowerBlit(screen, &src_rect, buffer, &src_rect); + + // Update the intermediate texture with the contents of the RGBA buffer. + SDL_UpdateTexture(sdl_texture, &src_rect, buffer->pixels, buffer->pitch); + + // Make sure the pillarboxes are kept clear each frame. + SDL_RenderClear(sdl_renderer); + + SDL_RenderCopy(sdl_renderer, sdl_texture, &src_rect, NULL); + + // Draw! + SDL_RenderPresent(sdl_renderer); +} + +// +// I_ScreenShot - moved to i_sshot.c +// + +// +// I_SetPalette +// +void I_SetPalette (int pal) +{ + newpal = pal; +} + +// I_PreInitGraphics + +static void I_ShutdownSDL(void) +{ + if (sdl_glcontext) SDL_GL_DeleteContext(sdl_glcontext); + if (screen) SDL_FreeSurface(screen); + if (buffer) SDL_FreeSurface(buffer); + if (sdl_texture) SDL_DestroyTexture(sdl_texture); + if (sdl_renderer) SDL_DestroyRenderer(sdl_renderer); + if (sdl_window) SDL_DestroyWindow(sdl_window); + + SDL_Quit(); + return; +} + +void I_PreInitGraphics(void) +{ + int p; + + // Initialize SDL + unsigned int flags = 0; + if (!(M_CheckParm("-nodraw") && M_CheckParm("-nosound"))) + flags = SDL_INIT_VIDEO; +#ifdef PRBOOM_DEBUG + flags |= SDL_INIT_NOPARACHUTE; +#endif + + p = SDL_Init(flags); + if (p < 0) + { + I_Error("Could not initialize SDL [%s]", SDL_GetError()); + } + + I_AtExit(I_ShutdownSDL, true); +} + +// e6y: resolution limitation is removed +static void I_InitBuffersRes(void) +{ + R_InitMeltRes(); + R_InitSpritesRes(); + R_InitBuffersRes(); + R_InitPlanesRes(); + R_InitVisplanesRes(); +} + +#define MAX_RESOLUTIONS_COUNT 128 +const char *screen_resolutions_list[MAX_RESOLUTIONS_COUNT] = {NULL}; +const char *screen_resolution = NULL; + +// +// I_GetScreenResolution +// Get current resolution from the config variable (WIDTHxHEIGHT format) +// 640x480 if screen_resolution variable has wrong data +// +static void I_GetScreenResolution(void) +{ + int width, height; + + desired_screenwidth = 640; + desired_screenheight = 480; + + if (screen_resolution) + { + if (sscanf(screen_resolution, "%dx%d", &width, &height) == 2) + { + desired_screenwidth = width; + desired_screenheight = height; + } + } + + // never exceed desktop resolution in fullscreen desktop mode + if (!exclusive_fullscreen) + { + desired_screenwidth = MIN(desired_screenwidth, desktop_mode.w); + desired_screenheight = MIN(desired_screenheight, desktop_mode.h); + } +} + +// make sure the canonical resolutions are always available +static const struct { + const int w, h; +} canonicals[] = { + { 640, 480}, // Doom 95 + { 320, 240}, // Doom 95 + {1120, 400}, // 21:9 + { 854, 400}, // 16:9 + { 768, 400}, // 16:10 + { 640, 400}, // MBF + { 560, 200}, // 21:9 + { 426, 200}, // 16:9 + { 384, 200}, // 16:10 + { 320, 200}, // Vanilla Doom +}; +static const int num_canonicals = sizeof(canonicals)/sizeof(*canonicals); + +// [FG] sort resolutions by width first and height second +static int cmp_resolutions (const void *a, const void *b) +{ + const char *const *sa = (const char *const *) a; + const char *const *sb = (const char *const *) b; + + int wa, wb, ha, hb; + + if (sscanf(*sa, "%dx%d", &wa, &ha) != 2) wa = ha = 0; + if (sscanf(*sb, "%dx%d", &wb, &hb) != 2) wb = hb = 0; + + return (wa == wb) ? ha - hb : wa - wb; +} + +// +// I_FillScreenResolutionsList +// Get all the supported screen resolutions +// and fill the list with them +// +static void I_FillScreenResolutionsList(void) +{ + SDL_DisplayMode mode; + int i, j, list_size, current_resolution_index, count; + char mode_name[256]; + + // do it only once + if (screen_resolutions_list[0]) + { + return; + } + + if (desired_screenwidth == 0 || desired_screenheight == 0) + { + I_GetScreenResolution(); + } + + // Don't call SDL_ListModes if SDL has not been initialized + count = 0; + if (!nodrawers) + count = SDL_GetNumDisplayModes(display_index); + + list_size = 0; + current_resolution_index = -1; + + // on success, SDL_GetNumDisplayModes() always returns at least 1 + if (count > 0) + { + // -2 for the desired resolution and for NULL + count = MIN(count, MAX_RESOLUTIONS_COUNT - 2 - num_canonicals); + + for(i = count - 1 + num_canonicals; i >= 0; i--) + { + int in_list = false; + + // make sure the canonical resolutions are always available + if (i > count - 1) + { + // no hard-coded resolutions for mode-changing fullscreen + if (exclusive_fullscreen) + continue; + + mode.w = canonicals[i - count].w; + mode.h = canonicals[i - count].h; + } + else + { + SDL_GetDisplayMode(display_index, i, &mode); + } + + // never exceed desktop resolution in fullscreen desktop mode + if (!exclusive_fullscreen) + if (mode.w > desktop_mode.w || mode.h > desktop_mode.h) + continue; + + doom_snprintf(mode_name, sizeof(mode_name), "%dx%d", mode.w, mode.h); + + for(j = 0; j < list_size; j++) + { + if (!strcmp(mode_name, screen_resolutions_list[j])) + { + in_list = true; + break; + } + } + + if (!in_list) + { + screen_resolutions_list[list_size] = strdup(mode_name); + + if (mode.w == desired_screenwidth && mode.h == desired_screenheight) + { + current_resolution_index = list_size; + } + + list_size++; + } + } + screen_resolutions_list[list_size] = NULL; + } + + // [FG] if the desired resolution not in the list, append it + doom_snprintf(mode_name, sizeof(mode_name), "%dx%d", desired_screenwidth, desired_screenheight); + + if (current_resolution_index == -1) + { + screen_resolutions_list[list_size] = strdup(mode_name); + list_size++; + } + + // [FG] sort the list + SDL_qsort(screen_resolutions_list, list_size, sizeof(*screen_resolutions_list), cmp_resolutions); + + // [FG] find the desired resolution again + for (i = 0; i < list_size; i++) + { + if (!strcmp(mode_name, screen_resolutions_list[i])) + { + current_resolution_index = i; + break; + } + } + + assert(list_size > 0); + assert(current_resolution_index > -1); + + screen_resolutions_list[list_size] = NULL; + screen_resolution = screen_resolutions_list[current_resolution_index]; +} + +// e6y +// GLBoom use this function for trying to set the closest supported resolution if the requested mode can't be set correctly. +// For example glboom.exe -geom 1025x768 -nowindow will set 1024x768. +// It should be used only for fullscreen modes. +static void I_ClosestResolution (int *width, int *height) +{ + int twidth, theight; + int cwidth = 0, cheight = 0; + int i, count; + unsigned int closest = UINT_MAX; + unsigned int dist; + + if (!SDL_WasInit(SDL_INIT_VIDEO)) + return; + + count = SDL_GetNumDisplayModes(display_index); + + if (count > 0) + { + for(i=0; i count1 ? pitch2 : pitch1); + + lprintf(LO_INFO, " optimized screen pitch is %d\n", SCREENPITCH); + } + else + { + SCREENPITCH = SCREENWIDTH * V_GetPixelDepth(); + } + } +} + +// CPhipps - +// I_InitScreenResolution +// Sets the screen resolution +void I_InitScreenResolution(void) +{ + int i, p, w, h; + char c, x; + video_mode_t mode; + int init = (sdl_window == NULL); + + I_GetScreenResolution(); + + if (init) + { + //e6y: ability to change screen resolution from GUI + I_FillScreenResolutionsList(); + + // Video stuff + if ((p = M_CheckParm("-width"))) + if (myargv[p+1]) + desired_screenwidth = atoi(myargv[p+1]); + + if ((p = M_CheckParm("-height"))) + if (myargv[p+1]) + desired_screenheight = atoi(myargv[p+1]); + + if ((p = M_CheckParm("-fullscreen"))) + use_fullscreen = 1; + + if ((p = M_CheckParm("-nofullscreen"))) + use_fullscreen = 0; + + // e6y + // New command-line options for setting a window (-window) + // or fullscreen (-nowindow) mode temporarily which is not saved in cfg. + // It works like "-geom" switch + desired_fullscreen = use_fullscreen; + if ((p = M_CheckParm("-window"))) + desired_fullscreen = 0; + + if ((p = M_CheckParm("-nowindow"))) + desired_fullscreen = 1; + + // e6y + // change the screen size for the current session only + // syntax: -geom WidthxHeight[w|f] + // examples: -geom 320x200f, -geom 640x480w, -geom 1024x768 + w = desired_screenwidth; + h = desired_screenheight; + + if (!(p = M_CheckParm("-geom"))) + p = M_CheckParm("-geometry"); + + if (p && p + 1 < myargc) + { + int count = sscanf(myargv[p+1], "%d%c%d%c", &w, &x, &h, &c); + + // at least width and height must be specified + // restoring original values if not + if (count < 3 || tolower(x) != 'x') + { + w = desired_screenwidth; + h = desired_screenheight; + } + else + { + if (count >= 4) + { + if (tolower(c) == 'w') + desired_fullscreen = 0; + if (tolower(c) == 'f') + desired_fullscreen = 1; + } + } + } + } + else + { + w = desired_screenwidth; + h = desired_screenheight; + } + + mode = (video_mode_t)I_GetModeFromString(default_videomode); + if ((i=M_CheckParm("-vidmode")) && i= 640x480 + while (screen_multiply*SCREENWIDTH < 640 || screen_multiply*actualheight < 480) + { + screen_multiply++; + } + + // [FG] apply screen_multiply to initial window size + if (!desired_fullscreen) + { + SDL_SetWindowSize(sdl_window, screen_multiply*SCREENWIDTH, screen_multiply*actualheight); + } + + // [FG] force integer scales + SDL_RenderSetIntegerScale(sdl_renderer, integer_scaling); + + screen = SDL_CreateRGBSurface(0, SCREENWIDTH, SCREENHEIGHT, V_GetNumPixelBits(), 0, 0, 0, 0); + buffer = SDL_CreateRGBSurface(0, SCREENWIDTH, SCREENHEIGHT, 32, 0, 0, 0, 0); + SDL_FillRect(buffer, NULL, 0); + + sdl_texture = SDL_CreateTextureFromSurface(sdl_renderer, buffer); + + if(screen == NULL) { + I_Error("Couldn't set %dx%d video mode [%s]", SCREENWIDTH, SCREENHEIGHT, SDL_GetError()); + } + } + + display_index = SDL_GetWindowDisplayIndex(sdl_window); + SDL_GetDesktopDisplayMode(display_index, &desktop_mode); + + if (sdl_video_window_pos) + { + int x, y; + if (sscanf(sdl_video_window_pos, "%d,%d", &x, &y) == 2) + { + SDL_SetWindowPosition(sdl_window, x, y); + } + if (strcmp(sdl_video_window_pos, "center") == 0) + { + SDL_SetWindowPosition(sdl_window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + } + } + + // Workaround for SDL 2.0.14 alt-tab bug (taken from Doom Retro) +#if defined(_WIN32) +{ + SDL_version ver; + SDL_GetVersion(&ver); + if (ver.major == 2 && ver.minor == 0 && (ver.patch == 14 || ver.patch == 16)) + { + SDL_SetHintWithPriority(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "1", SDL_HINT_OVERRIDE); + } +} +#endif + + windowid = SDL_GetWindowID(sdl_window); + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + SDL_GL_SetSwapInterval(((render_vsync && !novsync) ? 1 : 0)); + } +#endif + +#ifdef GL_DOOM + /*if (V_GetMode() == VID_MODEGL) + gld_MultisamplingCheck();*/ +#endif + + if (V_GetMode() != VID_MODEGL) + { + lprintf(LO_INFO, "I_UpdateVideoMode: 0x%x, %s, %s\n", init_flags, screen && screen->pixels ? "SDL buffer" : "own buffer", screen && SDL_MUSTLOCK(screen) ? "lock-and-copy": "direct access"); + + // Get the info needed to render to the display + if (!SDL_MUSTLOCK(screen)) + { + screens[0].not_on_heap = true; + screens[0].data = (unsigned char *) (screen->pixels); + screens[0].byte_pitch = screen->pitch; + screens[0].short_pitch = screen->pitch / V_GetModePixelDepth(VID_MODE16); + screens[0].int_pitch = screen->pitch / V_GetModePixelDepth(VID_MODE32); + } + else + { + screens[0].not_on_heap = false; + } + + V_AllocScreens(); + + R_InitBuffer(SCREENWIDTH, SCREENHEIGHT); + } + + // e6y: wide-res + // Need some initialisations before level precache + R_ExecuteSetViewSize(); + + V_SetPalette(0); + I_UploadNewPalette(0, true); + + ST_SetResolution(); + AM_SetResolution(); + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + int temp; + lprintf(LO_INFO,"SDL OpenGL PixelFormat:\n"); + SDL_GL_GetAttribute( SDL_GL_RED_SIZE, &temp ); + lprintf(LO_INFO," SDL_GL_RED_SIZE: %i\n",temp); + SDL_GL_GetAttribute( SDL_GL_GREEN_SIZE, &temp ); + lprintf(LO_INFO," SDL_GL_GREEN_SIZE: %i\n",temp); + SDL_GL_GetAttribute( SDL_GL_BLUE_SIZE, &temp ); + lprintf(LO_INFO," SDL_GL_BLUE_SIZE: %i\n",temp); + SDL_GL_GetAttribute( SDL_GL_STENCIL_SIZE, &temp ); + lprintf(LO_INFO," SDL_GL_STENCIL_SIZE: %i\n",temp); + SDL_GL_GetAttribute( SDL_GL_ACCUM_RED_SIZE, &temp ); + lprintf(LO_INFO," SDL_GL_ACCUM_RED_SIZE: %i\n",temp); + SDL_GL_GetAttribute( SDL_GL_ACCUM_GREEN_SIZE, &temp ); + lprintf(LO_INFO," SDL_GL_ACCUM_GREEN_SIZE: %i\n",temp); + SDL_GL_GetAttribute( SDL_GL_ACCUM_BLUE_SIZE, &temp ); + lprintf(LO_INFO," SDL_GL_ACCUM_BLUE_SIZE: %i\n",temp); + SDL_GL_GetAttribute( SDL_GL_ACCUM_ALPHA_SIZE, &temp ); + lprintf(LO_INFO," SDL_GL_ACCUM_ALPHA_SIZE: %i\n",temp); + SDL_GL_GetAttribute( SDL_GL_DOUBLEBUFFER, &temp ); + lprintf(LO_INFO," SDL_GL_DOUBLEBUFFER: %i\n",temp); + SDL_GL_GetAttribute( SDL_GL_BUFFER_SIZE, &temp ); + lprintf(LO_INFO," SDL_GL_BUFFER_SIZE: %i\n",temp); + SDL_GL_GetAttribute( SDL_GL_DEPTH_SIZE, &temp ); + lprintf(LO_INFO," SDL_GL_DEPTH_SIZE: %i\n",temp); + SDL_GL_GetAttribute( SDL_GL_MULTISAMPLESAMPLES, &temp ); + lprintf(LO_INFO," SDL_GL_MULTISAMPLESAMPLES: %i\n",temp); + SDL_GL_GetAttribute( SDL_GL_MULTISAMPLEBUFFERS, &temp ); + lprintf(LO_INFO," SDL_GL_MULTISAMPLEBUFFERS: %i\n",temp); + SDL_GL_GetAttribute( SDL_GL_STENCIL_SIZE, &temp ); + lprintf(LO_INFO," SDL_GL_STENCIL_SIZE: %i\n",temp); + + gld_Init(SCREENWIDTH, SCREENHEIGHT); + } + + if (V_GetMode() == VID_MODEGL) + { + M_ChangeFOV(); + deh_changeCompTranslucency(); + } +#endif + + src_rect.w = SCREENWIDTH; + src_rect.h = SCREENHEIGHT; +} + +static void ActivateMouse(void) +{ + SDL_SetRelativeMouseMode(SDL_TRUE); + SDL_GetRelativeMouseState(NULL, NULL); +} + +static void DeactivateMouse(void) +{ + SDL_SetRelativeMouseMode(SDL_FALSE); +} + +// +// 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. + +static void SmoothMouse(int* x, int* y) +{ + static int x_remainder_old = 0; + static int y_remainder_old = 0; + + int x_remainder, y_remainder; + fixed_t correction_factor; + + const fixed_t fractic = I_TickElapsedTime(); + + *x += x_remainder_old; + *y += y_remainder_old; + + correction_factor = FixedDiv(fractic, FRACUNIT + fractic); + + x_remainder = FixedMul(*x, correction_factor); + *x -= x_remainder; + x_remainder_old = x_remainder; + + y_remainder = FixedMul(*y, correction_factor); + *y -= y_remainder; + y_remainder_old = y_remainder; +} + +static void I_ReadMouse(void) +{ + if (mouse_enabled && window_focused) + { + int x, y; + + SDL_GetRelativeMouseState(&x, &y); + SmoothMouse(&x, &y); + + if (x != 0 || y != 0) + { + event_t event; + event.type = ev_mousemotion; + event.data1 = 0; + event.data2 = x << 4; + event.data3 = -y << 4; + + D_PostEvent(&event); + } + } + + if (!usemouse) + return; + + if (!MouseShouldBeGrabbed()) + { + mouse_currently_grabbed = false; + return; + } + + if (!mouse_currently_grabbed && !desired_fullscreen) + { + mouse_currently_grabbed = true; + } +} + +static dboolean MouseShouldBeGrabbed() +{ + // never grab the mouse when in screensaver mode + + //if (screensaver_mode) + // return false; + + // if the window doesnt have focus, never grab it + if (!window_focused) + return false; + + // always grab the mouse when full screen (dont want to + // see the mouse pointer) + if (desired_fullscreen) + return true; + + // if we specify not to grab the mouse, never grab + if (!mouse_enabled) + return false; + + // always grab the mouse in camera mode when playing levels + // and menu is not active + if (walkcamera.type) + return (demoplayback && gamestate == GS_LEVEL && !menuactive); + + // when menu is active or game is paused, release the mouse + if (menuactive || paused) + return false; + + // only grab mouse when playing levels (but not demos) + return (gamestate == GS_LEVEL) && !demoplayback; +} + +// Update the value of window_focused when we get a focus event +// +// We try to make ourselves be well-behaved: the grab on the mouse +// is removed if we lose focus (such as a popup window appearing), +// and we dont move the mouse around if we aren't focused either. +static void UpdateFocus(void) +{ + Uint32 flags = 0; + + window_focused = false; + if(sdl_window) + { + flags = SDL_GetWindowFlags(sdl_window); + if ((flags & SDL_WINDOW_SHOWN) && !(flags & SDL_WINDOW_MINIMIZED) && (flags & SDL_WINDOW_INPUT_FOCUS)) + { + window_focused = true; + } + } + + // e6y + // Reuse of a current palette to avoid black screen at software fullscreen modes + // after switching to OS and back + if (desired_fullscreen && window_focused) + { + // currentPaletteIndex? + if (st_palette < 0) + st_palette = 0; + + V_SetPalette(st_palette); + } + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + if (gl_hardware_gamma) + { + if (!window_focused) + { + // e6y: Restore of startup gamma if window loses focus + gld_SetGammaRamp(-1); + } + else + { + gld_SetGammaRamp(useglgamma); + } + } + } +#endif + + // Should the screen be grabbed? + // screenvisible = (state & SDL_APPACTIVE) != 0; +} + +void UpdateGrab(void) +{ + static dboolean currently_grabbed = false; + dboolean grab; + + grab = MouseShouldBeGrabbed(); + + if (grab && !currently_grabbed) + { + ActivateMouse(); + } + + if (!grab && currently_grabbed) + { + DeactivateMouse(); + } + + currently_grabbed = grab; +} + +static void ApplyWindowResize(SDL_Event *resize_event) +{ +} diff --git a/src/TEXTSCREEN/doomkeys.h b/src/TEXTSCREEN/doomkeys.h new file mode 100644 index 0000000..8c301c4 --- /dev/null +++ b/src/TEXTSCREEN/doomkeys.h @@ -0,0 +1,100 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// Key definitions +// +//----------------------------------------------------------------------------- + +#ifndef __DOOMKEYS__ +#define __DOOMKEYS__ + +// +// 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 '\b' +#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_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 0 +#define KEYP_1 KEY_END +#define KEYP_2 KEY_DOWNARROW +#define KEYP_3 KEY_PGDN +#define KEYP_4 KEY_LEFTARROW +#define KEYP_5 '5' +#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 + +#endif // __DOOMKEYS__ + diff --git a/src/TEXTSCREEN/txt_font.h b/src/TEXTSCREEN/txt_font.h new file mode 100644 index 0000000..ec88fe9 --- /dev/null +++ b/src/TEXTSCREEN/txt_font.h @@ -0,0 +1,547 @@ +// +// Copyright (C) 2005-2014 Simon Howard +// Copyright (C) 2002-2004 The DOSBox Team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// +// VGA font data +// Font data is from the DOSBox project (http://dosbox.sourceforge.net/) +// + +#ifndef __FONT_H__ +#define __FONT_H__ + +static unsigned char main_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, 0x6c, 0xfe, 0xfe, 0xfe, + 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, + 0x7c, 0x38, 0x10, 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, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, + 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, + 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, + 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, + 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, + 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, + 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, + 0x1e, 0x0e, 0x06, 0x02, 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, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, + 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xfe, 0xfe, 0xfe, 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, 0x0c, 0xfe, + 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, + 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, + 0xc0, 0xfe, 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, 0x10, 0x38, 0x38, 0x7c, + 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, + 0x38, 0x38, 0x10, 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, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, + 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, + 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, + 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x18, 0x30, 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, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, + 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, 0x02, 0x06, 0x0c, 0x18, + 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, + 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, + 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, + 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, + 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, + 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, + 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, + 0x06, 0x06, 0x0c, 0x78, 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, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, + 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, + 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, + 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, + 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, + 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, + 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, + 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, + 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, + 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, + 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, + 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, + 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, + 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, + 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 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, 0x86, 0x0c, 0x18, 0x30, + 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, + 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 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, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, + 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, + 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, + 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, + 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, + 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, + 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, + 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 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, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, + 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, + 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, + 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, + 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, + 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, + 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, + 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, + 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, + 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, + 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, + 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, + 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, + 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, + 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, + 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, + 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, + 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, + 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, + 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, + 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, + 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, + 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, + 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, + 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, + 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, + 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, 0x00, 0x00, 0xf8, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, + 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, 0x1f, + 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, 0x1f, 0x18, 0x1f, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 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, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, + 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, 0x1f, 0x18, 0x1f, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 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, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, + 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, + 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, + 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, + 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 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, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, + 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, + 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 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, 0x03, 0x06, 0x7e, 0xdb, 0xdb, + 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, + 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, + 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, + 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, + 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, + 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xd8, 0xd8, 0xd8, 0x70, 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, 0x76, 0xdc, 0x00, + 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 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, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, + 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, + 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static txt_font_t main_font = +{ + main_font_data, + 8, // width + 16 // height +}; + +#endif /* __FONT_H__ */ + diff --git a/src/TEXTSCREEN/txt_largefont.h b/src/TEXTSCREEN/txt_largefont.h new file mode 100644 index 0000000..0fe54b0 --- /dev/null +++ b/src/TEXTSCREEN/txt_largefont.h @@ -0,0 +1,2591 @@ +// +// Copyright (C) 2005-2014 Simon Howard +// Copyright (C) 2002-2004 The DOSBox Team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// +// "High resolution" font. +// +// This is an enhanced version of the 8x16 font from txt_font.h that +// has been doubled in size to 16x32, and then manually tweaked. +// + +static uint8_t large_font_data[] = +{ + // 0: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 1: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0xfc, 0x7f, 0xfe, 0xe0, 0x07, 0xc0, 0x03, + 0xcc, 0x33, 0xcc, 0x33, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xcf, 0xf3, 0xcf, 0xf3, + 0xc7, 0xe3, 0xc3, 0xc3, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xe0, 0x07, 0x7f, 0xfe, 0x3f, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 2: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0xfc, 0x7f, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xf3, 0xcf, 0xf3, 0xcf, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf8, 0x1f, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x3f, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 3: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x70, 0x7c, 0xf8, 0xff, 0xfc, 0xff, 0xfc, + 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0x7f, 0xf8, + 0x7f, 0xf8, 0x3f, 0xf0, 0x3f, 0xf0, 0x1f, 0xe0, + 0x0f, 0xc0, 0x07, 0x80, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 4: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x07, 0x80, 0x0f, 0xc0, 0x1f, 0xe0, + 0x3f, 0xf0, 0x7f, 0xf8, 0xff, 0xfc, 0xff, 0xfc, + 0x7f, 0xf8, 0x3f, 0xf0, 0x1f, 0xe0, 0x0f, 0xc0, + 0x07, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 5: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x07, 0xe0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x7c, 0x3e, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, + 0xfc, 0x3f, 0x7c, 0x3e, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 6: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x07, 0xe0, + 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7f, 0xfe, 0x3b, 0xdc, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 7: + 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, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x07, 0xe0, 0x03, 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, + + // 8: + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0x3f, 0xf8, 0x1f, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + + // 9: + 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, 0x38, 0x1c, 0x30, 0x0c, 0x30, 0x0c, + 0x30, 0x0c, 0x30, 0x0c, 0x38, 0x1c, 0x3c, 0x3c, + 0x1f, 0xf8, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 10: + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xe0, 0x07, + 0xc3, 0xc3, 0xc7, 0xe3, 0xcf, 0xf3, 0xcf, 0xf3, + 0xcf, 0xf3, 0xcf, 0xf3, 0xc7, 0xe3, 0xc3, 0xc3, + 0xe0, 0x07, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + + // 11: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xfc, 0x03, 0xfc, 0x00, 0xfc, 0x00, 0xfc, + 0x01, 0xec, 0x03, 0xcc, 0x07, 0x8c, 0x0f, 0x0c, + 0x3f, 0xc0, 0x7f, 0xe0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0x7f, 0xe0, 0x3f, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 12: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x1f, 0xf8, 0x0f, 0xf0, + 0x03, 0xc0, 0x03, 0xc0, 0x3f, 0xfc, 0x3f, 0xfc, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 13: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x3f, 0x00, 0x7f, 0x00, + 0xff, 0x00, 0xff, 0x00, 0xfe, 0x00, 0x7c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 14: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0xff, 0x3f, 0xff, 0x3c, 0x0f, 0x3c, 0x0f, + 0x3f, 0xff, 0x3f, 0xff, 0x3c, 0x0f, 0x3c, 0x0f, + 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, + 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1f, 0x3c, 0x3f, + 0x7c, 0x3f, 0xfc, 0x3f, 0xfc, 0x3e, 0xfc, 0x1c, + 0xf8, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 15: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0xc3, 0xc3, 0xf3, 0xcf, + 0x3f, 0xfc, 0x0f, 0xf0, 0xfc, 0x3f, 0xfc, 0x3f, + 0x0f, 0xf0, 0x3f, 0xfc, 0xf3, 0xcf, 0xc3, 0xc3, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 16: + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x00, + 0xe0, 0x00, 0xf0, 0x00, 0xf8, 0x00, 0xfc, 0x00, + 0xfe, 0x00, 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, + 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xc0, 0xff, 0x80, + 0xff, 0x00, 0xfe, 0x00, 0xfc, 0x00, 0xf8, 0x00, + 0xf0, 0x00, 0xe0, 0x00, 0xc0, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 17: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, + 0x00, 0x1c, 0x00, 0x3c, 0x00, 0x7c, 0x00, 0xfc, + 0x01, 0xfc, 0x03, 0xfc, 0x07, 0xfc, 0x0f, 0xfc, + 0x1f, 0xfc, 0x1f, 0xfc, 0x0f, 0xfc, 0x07, 0xfc, + 0x03, 0xfc, 0x01, 0xfc, 0x00, 0xfc, 0x00, 0x7c, + 0x00, 0x3c, 0x00, 0x1c, 0x00, 0x0c, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 18: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, + 0x1f, 0xf8, 0x3f, 0xfc, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x3f, 0xfc, 0x1f, 0xf8, 0x0f, 0xf0, 0x07, 0xe0, + 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 19: + 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, + + // 20: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0xff, 0x7f, 0xff, 0xe3, 0xcf, 0xe3, 0xcf, + 0xe3, 0xcf, 0xe3, 0xcf, 0xe3, 0xcf, 0xe3, 0xcf, + 0x7f, 0xcf, 0x3f, 0xcf, 0x03, 0xcf, 0x03, 0xcf, + 0x03, 0xcf, 0x03, 0xcf, 0x03, 0xcf, 0x03, 0xcf, + 0x03, 0xcf, 0x03, 0xcf, 0x03, 0xcf, 0x03, 0xcf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 21: + 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf0, 0x7f, 0xf8, + 0xf0, 0x3c, 0xf0, 0x1c, 0x7c, 0x00, 0x3f, 0x00, + 0x1f, 0xc0, 0x0f, 0xe0, 0x1c, 0xf0, 0x38, 0x78, + 0x70, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x38, + 0x78, 0x70, 0x3c, 0xe0, 0x1f, 0xc0, 0x0f, 0xc0, + 0x03, 0xf0, 0x00, 0xf8, 0xe0, 0x7c, 0xf0, 0x3c, + 0x7f, 0xf8, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 22: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, + 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 23: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, + 0x1f, 0xf8, 0x3f, 0xfc, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x3f, 0xfc, 0x1f, 0xf8, 0x0f, 0xf0, 0x07, 0xe0, + 0x03, 0xc0, 0x01, 0x80, 0x3f, 0xfc, 0x3f, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 24: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, + 0x1f, 0xf8, 0x3f, 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, 0xc0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 25: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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, 0x3f, 0xfc, 0x1f, 0xf8, + 0x0f, 0xf0, 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 26: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x01, 0xc0, + 0x00, 0xe0, 0x00, 0x70, 0xff, 0xf8, 0xff, 0xf8, + 0x00, 0x70, 0x00, 0xe0, 0x01, 0xc0, 0x03, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 27: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, + 0x1c, 0x00, 0x38, 0x00, 0x7f, 0xfc, 0x7f, 0xfc, + 0x38, 0x00, 0x1c, 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, + + // 28: + 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, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xff, 0xfc, 0xff, 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, + + // 29: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0x0c, 0x30, + 0x1c, 0x38, 0x3c, 0x3c, 0x7f, 0xfe, 0x7f, 0xfe, + 0x3c, 0x3c, 0x1c, 0x38, 0x0c, 0x30, 0x04, 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, + + // 30: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x07, 0x80, 0x07, 0x80, + 0x0f, 0xc0, 0x0f, 0xc0, 0x1f, 0xe0, 0x1f, 0xe0, + 0x3f, 0xf0, 0x3f, 0xf0, 0x7f, 0xf8, 0x7f, 0xf8, + 0xff, 0xfc, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 31: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xfc, 0xff, 0xfc, 0x7f, 0xf8, 0x7f, 0xf8, + 0x3f, 0xf0, 0x3f, 0xf0, 0x1f, 0xe0, 0x1f, 0xe0, + 0x0f, 0xc0, 0x0f, 0xc0, 0x07, 0x80, 0x07, 0x80, + 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 32: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 33: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x07, 0xe0, 0x07, 0xe0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 34: + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x1c, 0x38, 0x0c, 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, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 35: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0xe0, 0x1c, 0xe0, + 0x1c, 0xe0, 0x1c, 0xe0, 0xff, 0xfc, 0xff, 0xfc, + 0x1c, 0xe0, 0x1c, 0xe0, 0x1c, 0xe0, 0x1c, 0xe0, + 0x1c, 0xe0, 0x1c, 0xe0, 0xff, 0xfc, 0xff, 0xfc, + 0x1c, 0xe0, 0x1c, 0xe0, 0x1c, 0xe0, 0x1c, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 36: + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x3f, 0xf0, 0x7f, 0xf8, 0xf0, 0x3c, 0xf0, 0x1c, + 0xf0, 0x0c, 0xf0, 0x0c, 0xf0, 0x00, 0xf0, 0x00, + 0x7f, 0xf0, 0x3f, 0xf8, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0xc0, 0x3c, 0xc0, 0x3c, + 0xe0, 0x3c, 0xf0, 0x3c, 0x7f, 0xf8, 0x3f, 0xf0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 37: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x0c, 0xf0, 0x1c, 0xf0, 0x3c, 0x60, 0x78, + 0x00, 0xf0, 0x01, 0xe0, 0x03, 0xc0, 0x07, 0x80, + 0x0f, 0x00, 0x1e, 0x00, 0x3c, 0x00, 0x78, 0x00, + 0xf0, 0x18, 0xe0, 0x3c, 0xc0, 0x3c, 0x80, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 38: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0xc0, 0x1f, 0xe0, 0x3c, 0xf0, 0x3c, 0xf0, + 0x3c, 0xf0, 0x1c, 0xe0, 0x0f, 0xc0, 0x0f, 0xc0, + 0x3f, 0x0c, 0x7f, 0x1c, 0xf3, 0xf8, 0xf3, 0xf8, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf0, 0x7f, 0xfc, 0x3f, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 39: + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 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, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 40: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x01, 0xf0, 0x03, 0xc0, 0x07, 0x80, + 0x07, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, + 0x07, 0x80, 0x03, 0xc0, 0x01, 0xf0, 0x00, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 41: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x0f, 0x80, 0x03, 0xc0, 0x01, 0xe0, + 0x00, 0xe0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xf0, + 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, + 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xe0, 0x00, 0xe0, + 0x01, 0xe0, 0x03, 0xc0, 0x0f, 0x80, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 42: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x1c, 0x3c, 0x3c, 0x1e, 0x78, + 0x0f, 0xf0, 0x0f, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0x0f, 0xf0, 0x0f, 0xf0, 0x1e, 0x78, 0x3c, 0x3c, + 0x38, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 43: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x3f, 0xfc, 0x3f, 0xfc, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 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, + + // 44: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x07, 0x80, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 45: + 0x00, 0x00, 0x00, 0x00, 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, 0xfc, 0xff, 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, + + // 46: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 47: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x0c, 0x00, 0x1c, 0x00, 0x3c, + 0x00, 0x78, 0x00, 0xf0, 0x01, 0xe0, 0x03, 0xc0, + 0x07, 0x80, 0x0f, 0x00, 0x1e, 0x00, 0x3c, 0x00, + 0x78, 0x00, 0xf0, 0x00, 0xe0, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 48: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c, 0x78, 0x1e, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf1, 0x8f, 0xf3, 0xcf, 0xf3, 0xcf, 0xf1, 0x8f, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0x78, 0x1e, 0x3c, 0x3c, 0x1f, 0xf8, 0x0f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 49: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xc0, 0x03, 0xc0, 0x07, 0xc0, 0x0f, 0xc0, + 0x1f, 0xc0, 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, 0x3f, 0xfc, 0x3f, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 50: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0xe0, 0x3f, 0xf0, 0x70, 0x38, 0xe0, 0x3c, + 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0, 0x01, 0xe0, + 0x03, 0xc0, 0x07, 0x80, 0x0f, 0x00, 0x1e, 0x00, + 0x3c, 0x00, 0x38, 0x00, 0x70, 0x00, 0x70, 0x00, + 0xf0, 0x0c, 0xf0, 0x3c, 0xff, 0xfc, 0xff, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 51: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0xf0, 0x7f, 0xf8, 0xf8, 0x7c, 0xf0, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x0f, 0xf0, 0x0f, 0xf0, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0xf0, 0x3c, 0xf8, 0x7c, 0x7f, 0xf8, 0x3f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 52: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0xf0, 0x01, 0xf0, 0x03, 0xf0, + 0x07, 0xf0, 0x0f, 0xf0, 0x1e, 0xf0, 0x3c, 0xf0, + 0x78, 0xf0, 0xf0, 0xf0, 0xff, 0xfc, 0xff, 0xfc, + 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, + 0x00, 0xf0, 0x00, 0xf0, 0x03, 0xfc, 0x03, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 53: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xfc, 0xff, 0xfc, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xff, 0xf0, 0xff, 0xf8, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0x7f, 0xf8, 0x3f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 54: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0xc0, 0x1f, 0xc0, 0x3c, 0x00, 0x78, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xff, 0xf0, 0xff, 0xf8, 0xf8, 0x7c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf8, 0x7c, 0x7f, 0xf8, 0x3f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 55: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xfc, 0xff, 0xfc, 0xf0, 0x3c, 0xf0, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x78, 0x00, 0xf0, 0x01, 0xe0, 0x03, 0xc0, + 0x07, 0x80, 0x07, 0x80, 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, + + // 56: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0xf0, 0x7f, 0xf8, 0xf8, 0x7c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0x78, 0x78, + 0x3f, 0xf0, 0x3f, 0xf0, 0x78, 0x78, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf8, 0x7c, 0x7f, 0xf8, 0x3f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 57: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0xf0, 0x7f, 0xf8, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0x7f, 0xfc, 0x3f, 0xfc, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x78, 0x00, 0xf0, 0x3f, 0xe0, 0x3f, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 58: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x80, 0x03, 0xc0, 0x03, 0xc0, 0x01, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0xc0, + 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 59: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x80, 0x03, 0xc0, 0x03, 0xc0, 0x01, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x07, 0x80, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 60: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x78, + 0x00, 0xf0, 0x01, 0xe0, 0x03, 0xc0, 0x07, 0x80, + 0x0f, 0x00, 0x1e, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x1e, 0x00, 0x0f, 0x00, 0x07, 0x80, 0x03, 0xc0, + 0x01, 0xe0, 0x00, 0xf0, 0x00, 0x78, 0x00, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 61: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x3f, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0xfc, 0x3f, 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, + + // 62: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x1e, 0x00, + 0x0f, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01, 0xe0, + 0x00, 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x78, 0x00, 0xf0, 0x01, 0xe0, 0x03, 0xc0, + 0x07, 0x80, 0x0f, 0x00, 0x1e, 0x00, 0x3c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 63: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0xf0, 0x7f, 0xf8, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x7c, 0x00, 0xf8, 0x01, 0xf0, + 0x03, 0xe0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 64: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf0, 0x7f, 0xf8, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf1, 0xfc, 0xf3, 0xfc, 0xf3, 0xfc, 0xf3, 0xfc, + 0xf3, 0xfc, 0xf3, 0xfc, 0xf3, 0xf8, 0xf1, 0xf0, + 0xf0, 0x00, 0xf0, 0x00, 0x7f, 0xf0, 0x3f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 65: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x07, 0x80, 0x0f, 0xc0, 0x1f, 0xe0, + 0x3c, 0xf0, 0x78, 0x78, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xff, 0xfc, 0xff, 0xfc, + 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, + + // 66: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xe0, 0xff, 0xf0, 0x3c, 0x38, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x38, + 0x3f, 0xf0, 0x3f, 0xf0, 0x3c, 0x38, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x38, 0xff, 0xf0, 0xff, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 67: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xc0, 0x0f, 0xf0, 0x1e, 0x78, 0x3c, 0x3c, + 0x78, 0x1c, 0x78, 0x0c, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0x78, 0x0c, 0x78, 0x1c, + 0x3c, 0x3c, 0x1e, 0x78, 0x0f, 0xf0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 68: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x80, 0xff, 0xc0, 0x3d, 0xe0, 0x3c, 0xf0, + 0x3c, 0x78, 0x3c, 0x38, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x38, 0x3c, 0x78, + 0x3c, 0xf0, 0x3d, 0xe0, 0xff, 0xc0, 0xff, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 69: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xfc, 0xff, 0xfc, 0x3c, 0x3c, 0x3c, 0x1c, + 0x3c, 0x0c, 0x3c, 0x04, 0x3c, 0xc0, 0x3c, 0xc0, + 0x3f, 0xc0, 0x3f, 0xc0, 0x3c, 0xc0, 0x3c, 0xc0, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x04, 0x3c, 0x0c, + 0x3c, 0x1c, 0x3c, 0x3c, 0xff, 0xfc, 0xff, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 70: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xfc, 0xff, 0xfc, 0x3c, 0x3c, 0x3c, 0x1c, + 0x3c, 0x0c, 0x3c, 0x04, 0x3c, 0xc0, 0x3c, 0xc0, + 0x3f, 0xc0, 0x3f, 0xc0, 0x3c, 0xc0, 0x3c, 0xc0, + 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, + + // 71: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0xe0, 0x0f, 0xf0, 0x1e, 0x38, 0x3c, 0x1c, + 0x78, 0x0c, 0x70, 0x0c, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xf3, 0xfc, 0xf3, 0xfc, + 0xf0, 0x3c, 0xf0, 0x3c, 0x70, 0x3c, 0x78, 0x3c, + 0x3c, 0x3c, 0x1e, 0x7c, 0x0f, 0xec, 0x07, 0xcc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 72: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xff, 0xfc, 0xff, 0xfc, 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, + + // 73: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0xf0, 0x0f, 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, 0x0f, 0xf0, 0x0f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 74: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xfc, 0x03, 0xfc, 0x00, 0xf0, 0x00, 0xf0, + 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, + 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0x7f, 0xe0, 0x3f, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 75: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x3c, 0xfc, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x78, 0x3c, 0xf0, + 0x3f, 0xe0, 0x3f, 0xc0, 0x3f, 0xc0, 0x3f, 0xe0, + 0x3c, 0xf0, 0x3c, 0x78, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0xfc, 0x3c, 0xfc, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 76: + 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, 0x04, 0x3c, 0x0c, + 0x3c, 0x1c, 0x3c, 0x3c, 0xff, 0xfc, 0xff, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 77: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x07, 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf3, 0xcf, 0xf1, 0x8f, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 78: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x3c, 0xf0, 0x3c, 0xf8, 0x3c, 0xfc, 0x3c, + 0xfe, 0x3c, 0xff, 0x3c, 0xff, 0xfc, 0xff, 0xfc, + 0xf3, 0xfc, 0xf1, 0xfc, 0xf0, 0xfc, 0xf0, 0x7c, + 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, + + // 79: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0xe0, 0x3f, 0xf0, 0x78, 0x78, 0x78, 0x78, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0x78, 0x78, 0x78, 0x78, 0x3f, 0xf0, 0x1f, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 80: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xe0, 0xff, 0xf0, 0x3c, 0x78, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x78, + 0x3f, 0xf0, 0x3f, 0xe0, 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, + + // 81: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0xf0, 0x7f, 0xf8, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf3, 0x3c, 0xf3, 0x3c, + 0xf3, 0xfc, 0xf3, 0xfc, 0x7f, 0xf8, 0x3f, 0xf0, + 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xfc, 0x00, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 82: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xe0, 0xff, 0xf0, 0x3c, 0x38, 0x3c, 0x1c, + 0x3c, 0x1c, 0x3c, 0x1c, 0x3c, 0x3c, 0x3c, 0x38, + 0x3f, 0xf0, 0x3f, 0xe0, 0x3c, 0xe0, 0x3c, 0x70, + 0x3c, 0x38, 0x3c, 0x38, 0x3c, 0x1c, 0x3c, 0x1c, + 0x3c, 0x1c, 0x3c, 0x1c, 0xfc, 0x1c, 0xfc, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 83: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0xf0, 0x7f, 0xf8, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0x78, 0x00, 0x3c, 0x00, + 0x1f, 0x00, 0x0f, 0xc0, 0x03, 0xf0, 0x00, 0xf8, + 0x00, 0x3c, 0x00, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf8, 0x7c, 0x7f, 0xf8, 0x3f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 84: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xf3, 0xcf, 0xe3, 0xc7, + 0xc3, 0xc3, 0x83, 0xc1, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 85: + 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, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0x78, 0x78, 0x3f, 0xf0, 0x1f, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 86: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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, 0x0f, 0x78, 0x1e, 0x3c, 0x3c, + 0x1e, 0x78, 0x0f, 0xf0, 0x07, 0xe0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 87: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf1, 0x8f, 0xf1, 0x8f, + 0xf3, 0xcf, 0xf3, 0xcf, 0xf7, 0xef, 0xf7, 0xef, + 0x7e, 0x7e, 0x7e, 0x7e, 0x3c, 0x3c, 0x3c, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 88: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0x78, 0x1e, 0x3c, 0x3c, 0x1e, 0x78, 0x0f, 0xf0, + 0x07, 0xe0, 0x03, 0xc0, 0x03, 0xc0, 0x07, 0xe0, + 0x0f, 0xf0, 0x1e, 0x78, 0x3c, 0x3c, 0x78, 0x1e, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 89: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf0, 0x0f, 0x78, 0x1e, 0x3c, 0x3c, 0x1e, 0x78, + 0x0f, 0xf0, 0x07, 0xe0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 90: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xe0, 0x1e, + 0xc0, 0x3c, 0x80, 0x78, 0x00, 0xf0, 0x01, 0xe0, + 0x03, 0xc0, 0x07, 0x80, 0x0f, 0x00, 0x1e, 0x00, + 0x3c, 0x00, 0x78, 0x00, 0xf0, 0x01, 0xf0, 0x03, + 0xf0, 0x07, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 91: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 92: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xc0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xfc, 0x00, 0xfc, 0x00, + 0x3f, 0x00, 0x3f, 0x00, 0x0f, 0xc0, 0x0f, 0xc0, + 0x03, 0xf0, 0x03, 0xf0, 0x00, 0xfc, 0x00, 0xfc, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x0c, 0x00, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 93: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 94: + 0x00, 0x00, 0x03, 0x00, 0x07, 0x80, 0x0f, 0xc0, + 0x1c, 0xe0, 0x38, 0x70, 0x70, 0x38, 0xe0, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 95: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 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, + + // 96: + 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x07, 0x00, + 0x03, 0xc0, 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, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 97: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x80, 0x3f, 0xc0, + 0x00, 0xe0, 0x00, 0xf0, 0x1f, 0xf0, 0x3f, 0xf0, + 0x78, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0x79, 0xf0, 0x3f, 0x3c, 0x1e, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 98: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x00, 0xfc, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3f, 0x80, 0x3f, 0xc0, + 0x3c, 0xe0, 0x3c, 0x70, 0x3c, 0x38, 0x3c, 0x38, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x38, 0x3c, 0x38, 0x3f, 0xf0, 0x3f, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 99: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0x3f, 0xf0, + 0x78, 0x38, 0xf0, 0x3c, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x1c, 0x78, 0x38, 0x3f, 0xf0, 0x1f, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 100: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xf0, 0x03, 0xf0, 0x00, 0xf0, 0x00, 0xf0, + 0x00, 0xf0, 0x00, 0xf0, 0x0f, 0xf0, 0x1f, 0xf0, + 0x3c, 0xf0, 0x78, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf8, 0xf0, 0x7f, 0xbc, 0x3f, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 101: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf0, 0x7f, 0xf8, + 0xf0, 0x3c, 0xf0, 0x3c, 0xff, 0xfc, 0xff, 0xfc, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x1c, 0xf8, 0x3c, 0x7f, 0xf8, 0x3f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 102: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x80, 0x0f, 0xc0, 0x1c, 0xe0, 0x3c, 0x70, + 0x3c, 0x30, 0x3c, 0x30, 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, + + // 103: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1c, 0x3f, 0xbc, + 0x70, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0x70, 0xf0, 0x3f, 0xf0, 0x1f, 0xf0, + 0x00, 0xf0, 0x00, 0xf0, 0xe0, 0xf0, 0x70, 0xf0, + 0x3f, 0xc0, 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x00, + + // 104: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x00, 0xfc, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0xf0, 0x3d, 0xf8, + 0x3f, 0x3c, 0x3e, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0xfc, 0x3c, 0xfc, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 105: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x80, 0x03, 0xc0, 0x03, 0xc0, 0x01, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x0f, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 106: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0xfc, + 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, 0x1c, 0x38, + 0x0f, 0xf0, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, + + // 107: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x00, 0xfc, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x1c, 0x3c, 0x38, + 0x3c, 0x70, 0x3c, 0xe0, 0x3d, 0xc0, 0x3f, 0x80, + 0x3f, 0x80, 0x3f, 0xc0, 0x3c, 0xe0, 0x3c, 0x70, + 0x3c, 0x38, 0x3c, 0x1c, 0xfc, 0x1c, 0xfc, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 108: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0xc0, 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, 0xc0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 109: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3c, 0xfe, 0x7e, + 0xff, 0xff, 0xff, 0xff, 0xf3, 0xcf, 0xf3, 0xcf, + 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, + 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 110: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe0, 0xf7, 0xf0, + 0x3c, 0x38, 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, + + // 111: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0x3f, 0xf0, + 0x78, 0x78, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0x78, 0x78, 0x3f, 0xf0, 0x1f, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 112: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe0, 0xf7, 0xf0, + 0x3e, 0x78, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x78, 0x3f, 0xf0, 0x3f, 0xe0, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 113: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1c, 0x3f, 0xbc, + 0x78, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0x78, 0xf0, 0x3f, 0xf0, 0x1f, 0xf0, + 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, + 0x03, 0xfc, 0x03, 0xfc, 0x00, 0x00, 0x00, 0x00, + + // 114: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe1, 0xe0, 0xf3, 0xf0, + 0x3f, 0x38, 0x3e, 0x1c, 0x3c, 0x1c, 0x3c, 0x1c, + 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, + + // 115: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf0, 0x7f, 0xf8, + 0xf0, 0x3c, 0xf0, 0x3c, 0x7c, 0x00, 0x3f, 0x00, + 0x0f, 0xc0, 0x03, 0xe0, 0x00, 0xf0, 0x00, 0x78, + 0xf0, 0x3c, 0xf0, 0x3c, 0x7f, 0xf8, 0x3f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 116: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0xff, 0xf0, 0xff, 0xf0, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x1c, 0x07, 0x3c, 0x03, 0xf0, 0x01, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 117: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf0, 0x7f, 0xbc, 0x3f, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 118: + 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, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf0, 0x0f, 0xf0, 0x0f, 0x78, 0x1e, 0x3c, 0x3c, + 0x1e, 0x78, 0x0f, 0xf0, 0x07, 0xe0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 119: + 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, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, + 0xff, 0xff, 0xff, 0xff, 0x7e, 0x7e, 0x3c, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 120: + 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, 0x07, 0xe0, + 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 0x1e, 0x78, + 0x3c, 0x3c, 0x78, 0x1e, 0xf0, 0x0f, 0xe0, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 121: + 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, 0x70, 0x3c, 0x3f, 0xfc, 0x1f, 0xfc, + 0x00, 0x3c, 0x00, 0x38, 0x00, 0x70, 0x00, 0xe0, + 0xff, 0xc0, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, + + // 122: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xff, 0xf8, + 0xe0, 0x70, 0xc0, 0xe0, 0x01, 0xc0, 0x03, 0x80, + 0x07, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x38, 0x00, + 0x70, 0x0c, 0xe0, 0x1c, 0xff, 0xfc, 0xff, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 123: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0xfc, 0x01, 0xc0, 0x03, 0x80, + 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, + 0x3f, 0x00, 0x3f, 0x00, 0x03, 0x80, 0x03, 0x80, + 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, + 0x03, 0xc0, 0x03, 0xc0, 0x00, 0xfc, 0x00, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 124: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 125: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3f, 0x00, 0x03, 0x80, 0x01, 0xc0, + 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, + 0x00, 0xfc, 0x00, 0xfc, 0x01, 0xc0, 0x01, 0xc0, + 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, + 0x03, 0xc0, 0x03, 0x80, 0x3f, 0x00, 0x3e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 126: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x1c, 0x3f, 0x3c, 0x73, 0xf0, 0xe1, 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, + + // 127: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x07, 0x80, 0x0f, 0xc0, + 0x1c, 0xe0, 0x38, 0x70, 0x70, 0x38, 0xe0, 0x1c, + 0xe0, 0x1c, 0xe0, 0x1c, 0xe0, 0x1c, 0xe0, 0x1c, + 0xff, 0xfc, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 128: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0xe0, 0x0f, 0xf0, 0x1c, 0x38, 0x38, 0x1c, + 0x70, 0x0c, 0xf0, 0x0c, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x0c, 0x70, 0x0c, 0x38, 0x1c, 0x1c, 0x38, + 0x0f, 0xf0, 0x07, 0xf0, 0x00, 0xf0, 0x00, 0x70, + 0x00, 0x38, 0x00, 0x38, 0x3f, 0xf0, 0x3f, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 129: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf0, 0x7f, 0xbc, 0x3f, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 130: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xe0, + 0x01, 0xc0, 0x03, 0x80, 0x07, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf0, 0x7f, 0xf8, + 0xf0, 0x3c, 0xf0, 0x3c, 0xff, 0xfc, 0xff, 0xfc, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x1c, 0xf8, 0x3c, 0x7f, 0xf8, 0x3f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 131: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x07, 0x80, 0x0f, 0xc0, 0x1c, 0xe0, 0x38, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x80, 0x3f, 0xc0, + 0x00, 0xe0, 0x00, 0xf0, 0x1f, 0xf0, 0x3f, 0xf0, + 0x78, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0x79, 0xf0, 0x3f, 0x3c, 0x1e, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 132: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x80, 0x3f, 0xc0, + 0x00, 0xe0, 0x00, 0xf0, 0x1f, 0xf0, 0x3f, 0xf0, + 0x78, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0x79, 0xf0, 0x3f, 0x3c, 0x1e, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 133: + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x1c, 0x00, + 0x0e, 0x00, 0x07, 0x00, 0x03, 0x80, 0x01, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x80, 0x3f, 0xc0, + 0x00, 0xe0, 0x00, 0xf0, 0x1f, 0xf0, 0x3f, 0xf0, + 0x78, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0x79, 0xf0, 0x3f, 0x3c, 0x1e, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 134: + 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x0f, 0xc0, + 0x38, 0x70, 0x38, 0x70, 0x0f, 0xc0, 0x07, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x80, 0x3f, 0xc0, + 0x00, 0xe0, 0x00, 0xf0, 0x1f, 0xf0, 0x3f, 0xf0, + 0x78, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0x79, 0xf0, 0x3f, 0x3c, 0x1e, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 135: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0xe0, 0x0f, 0xf0, 0x1c, 0x38, 0x3c, 0x1c, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x1c, 0x1c, 0x38, 0x0f, 0xf0, 0x07, 0xf0, + 0x00, 0xf0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x38, + 0x0f, 0xf0, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 136: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x07, 0x80, 0x0f, 0xc0, 0x1c, 0xe0, 0x38, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf0, 0x7f, 0xf8, + 0xf0, 0x3c, 0xf0, 0x3c, 0xff, 0xfc, 0xff, 0xfc, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x1c, 0xf8, 0x3c, 0x7f, 0xf8, 0x3f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 137: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x3c, 0xf0, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf0, 0x7f, 0xf8, + 0xf0, 0x3c, 0xf0, 0x3c, 0xff, 0xfc, 0xff, 0xfc, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x1c, 0xf8, 0x3c, 0x7f, 0xf8, 0x3f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 138: + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x1c, 0x00, + 0x0e, 0x00, 0x07, 0x00, 0x03, 0x80, 0x01, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf0, 0x7f, 0xf8, + 0xf0, 0x3c, 0xf0, 0x3c, 0xff, 0xfc, 0xff, 0xfc, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x1c, 0xf8, 0x3c, 0x7f, 0xf8, 0x3f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 139: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x0f, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 140: + 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0xc0, + 0x07, 0xe0, 0x0e, 0x70, 0x1c, 0x38, 0x38, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x0f, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 141: + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x1c, 0x00, + 0x0e, 0x00, 0x07, 0x00, 0x03, 0x80, 0x01, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x0f, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 142: + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3c, 0xf0, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x07, 0x80, 0x0f, 0xc0, 0x1c, 0xe0, 0x38, 0x70, + 0x70, 0x38, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xff, 0xfc, 0xff, 0xfc, 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, + + // 143: + 0x07, 0x80, 0x0f, 0xc0, 0x38, 0x70, 0x38, 0x70, + 0x0f, 0xc0, 0x07, 0x80, 0x00, 0x00, 0x03, 0x00, + 0x07, 0x80, 0x0f, 0xc0, 0x1c, 0xe0, 0x38, 0x70, + 0x70, 0x38, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xff, 0xfc, 0xff, 0xfc, 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, + + // 144: + 0x01, 0xc0, 0x03, 0x80, 0x07, 0x00, 0x0e, 0x00, + 0x1c, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xfc, 0xff, 0xfc, 0x3c, 0x1c, 0x3c, 0x0c, + 0x3c, 0x00, 0x3c, 0x00, 0x3f, 0xf0, 0x3f, 0xf0, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x0c, 0x3c, 0x1c, 0xff, 0xfc, 0xff, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 145: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x38, 0x78, 0x1c, 0xfc, + 0x0f, 0xce, 0x07, 0xcf, 0x03, 0xcf, 0x03, 0xce, + 0x1f, 0xfc, 0x3f, 0xf8, 0x73, 0xc0, 0xe3, 0xc0, + 0xe3, 0xe0, 0x73, 0x78, 0x3f, 0x3f, 0x1e, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 146: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0xfc, 0x0f, 0xfc, 0x1c, 0xf0, 0x38, 0xf0, + 0x70, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xff, 0xfc, 0xff, 0xfc, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xfc, 0xf0, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 147: + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x07, 0x80, + 0x0f, 0xc0, 0x1c, 0xe0, 0x38, 0x70, 0x70, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0x3f, 0xf0, + 0x78, 0x78, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0x78, 0x78, 0x3f, 0xf0, 0x1f, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 148: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x3c, 0xf0, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0x3f, 0xf0, + 0x78, 0x78, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0x78, 0x78, 0x3f, 0xf0, 0x1f, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 149: + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x1c, 0x00, + 0x0e, 0x00, 0x07, 0x00, 0x03, 0x80, 0x01, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0x3f, 0xf0, + 0x78, 0x78, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0x78, 0x78, 0x3f, 0xf0, 0x1f, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 150: + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0f, 0x00, + 0x1f, 0x80, 0x39, 0xc0, 0x70, 0xe0, 0xe0, 0x70, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf0, 0x7f, 0xbc, 0x3f, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 151: + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x1c, 0x00, + 0x0e, 0x00, 0x07, 0x00, 0x03, 0x80, 0x01, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf0, 0x7f, 0xbc, 0x3f, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 152: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x3c, 0xf0, 0x3c, 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, 0x70, 0x3c, 0x3f, 0xfc, 0x1f, 0xfc, + 0x00, 0x3c, 0x00, 0x38, 0x00, 0x70, 0x00, 0xe0, + 0x3f, 0xc0, 0x3f, 0x80, 0x00, 0x00, 0x00, 0x00, + + // 153: + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3c, 0xf0, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0x3f, 0xf0, + 0x70, 0x38, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0x70, 0x38, 0x3f, 0xf0, 0x1f, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 154: + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3c, 0xf0, 0x3c, + 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, + 0xf0, 0x3c, 0x70, 0x38, 0x3f, 0xf0, 0x1f, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 155: + 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x1f, 0xf8, 0x3f, 0xfc, + 0x70, 0x0e, 0xe0, 0x07, 0xe0, 0x00, 0xe0, 0x00, + 0xe0, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0xe0, 0x00, + 0xe0, 0x07, 0x70, 0x0e, 0x3f, 0xfc, 0x1f, 0xf8, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 156: + 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x0f, 0xc0, + 0x1c, 0xe0, 0x3c, 0x70, 0x3c, 0x30, 0x3c, 0x30, + 0x3c, 0x00, 0x3c, 0x00, 0xff, 0x00, 0xff, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0xfc, 0x1c, 0xfc, 0x38, 0xff, 0xf0, 0xff, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 157: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x07, 0x70, 0x0e, 0x38, 0x1c, 0x1c, 0x38, + 0x0e, 0x70, 0x07, 0xe0, 0x03, 0xc0, 0x03, 0xc0, + 0xff, 0xff, 0xff, 0xff, 0x03, 0xc0, 0x03, 0xc0, + 0xff, 0xff, 0xff, 0xff, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 158: + 0x00, 0x00, 0x00, 0x00, 0xff, 0xf0, 0xff, 0xf8, + 0x3c, 0x3c, 0x3c, 0x1c, 0x3c, 0x1c, 0x3c, 0x3c, + 0x3f, 0xf8, 0x3f, 0xf0, 0x3c, 0x0c, 0x3c, 0x1c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0xff, 0x3c, 0xff, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0xff, 0x1f, 0xff, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 159: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xfc, + 0x01, 0xee, 0x03, 0xc7, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x3f, 0xfc, 0x3f, 0xfc, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0xe3, 0xc0, 0x77, 0x80, 0x3f, 0x00, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 160: + 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x03, 0x80, + 0x07, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x38, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x80, 0x3f, 0xc0, + 0x00, 0xe0, 0x00, 0xf0, 0x1f, 0xf0, 0x3f, 0xf0, + 0x78, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0x79, 0xf0, 0x3f, 0x3c, 0x1e, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 161: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xe0, + 0x01, 0xc0, 0x03, 0x80, 0x07, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x0f, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 162: + 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x03, 0x80, + 0x07, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x38, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0x3f, 0xf0, + 0x78, 0x78, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0x78, 0x78, 0x3f, 0xf0, 0x1f, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 163: + 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x03, 0x80, + 0x07, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x38, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf0, 0x7f, 0xbc, 0x3f, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 164: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x1c, 0x3f, 0x3c, 0xf3, 0xf0, 0xe1, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe0, 0xf7, 0xf0, + 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, + + // 165: + 0x1e, 0x1c, 0x3f, 0x3c, 0xf3, 0xf0, 0xe1, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x3c, 0xf0, 0x3c, + 0xf8, 0x3c, 0xfc, 0x3c, 0xfe, 0x3c, 0xff, 0x3c, + 0xff, 0xbc, 0xf7, 0xfc, 0xf3, 0xfc, 0xf1, 0xfc, + 0xf0, 0xfc, 0xf0, 0x7c, 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, + + // 166: + 0x00, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x0f, 0xf0, + 0x1c, 0xf0, 0x38, 0xf0, 0x38, 0xf0, 0x1c, 0xf0, + 0x0f, 0xfc, 0x07, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0xfc, 0x3f, 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, 0x00, 0x00, 0x00, + + // 167: + 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x0f, 0xc0, + 0x1c, 0xe0, 0x38, 0x70, 0x38, 0x70, 0x1c, 0xe0, + 0x0f, 0xc0, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0xf0, 0x3f, 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, + + // 168: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x1e, 0x00, 0x3c, 0x00, 0x78, 0x00, + 0xf0, 0x00, 0xe0, 0x00, 0xe0, 0x1c, 0xe0, 0x1c, + 0xe0, 0x1c, 0xf0, 0x3c, 0x7f, 0xf8, 0x3f, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 169: + 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, 0xfc, 0xff, 0xfc, 0xf0, 0x00, 0xf0, 0x00, + 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, + + // 170: + 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, 0xfc, 0xff, 0xfc, 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, + + // 171: + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x04, 0xf0, 0x0c, + 0xf0, 0x1c, 0xf0, 0x38, 0xf0, 0x70, 0xf0, 0xe0, + 0x01, 0xc0, 0x03, 0x80, 0x07, 0x00, 0x0e, 0x00, + 0x1c, 0x00, 0x38, 0x00, 0x70, 0x7c, 0xe0, 0xfe, + 0xc1, 0xcf, 0x83, 0x8f, 0x00, 0x1c, 0x00, 0x38, + 0x00, 0x70, 0x00, 0xe0, 0x01, 0xff, 0x03, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 172: + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x04, 0xf0, 0x0c, + 0xf0, 0x1c, 0xf0, 0x38, 0xf0, 0x70, 0xf0, 0xe0, + 0x01, 0xc0, 0x03, 0x80, 0x07, 0x00, 0x0e, 0x00, + 0x1c, 0x3c, 0x38, 0x7c, 0x70, 0xfc, 0xe1, 0xbc, + 0xc3, 0x3c, 0x86, 0x3c, 0x0f, 0xfc, 0x0f, 0xfc, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 173: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x07, 0xe0, 0x07, 0xe0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x07, 0xe0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 174: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x1e, 0x78, + 0x3c, 0xf0, 0x79, 0xe0, 0xf3, 0xc0, 0xf3, 0xc0, + 0x79, 0xe0, 0x3c, 0xf0, 0x1e, 0x78, 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, + + // 175: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf3, 0xc0, 0x79, 0xe0, + 0x3c, 0xf0, 0x1e, 0x78, 0x0f, 0x3c, 0x0f, 0x3c, + 0x1e, 0x78, 0x3c, 0xf0, 0x79, 0xe0, 0xf3, 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, + + // 176: + 0x03, 0x03, 0x03, 0x03, 0x30, 0x30, 0x30, 0x30, + 0x03, 0x03, 0x03, 0x03, 0x30, 0x30, 0x30, 0x30, + 0x03, 0x03, 0x03, 0x03, 0x30, 0x30, 0x30, 0x30, + 0x03, 0x03, 0x03, 0x03, 0x30, 0x30, 0x30, 0x30, + 0x03, 0x03, 0x03, 0x03, 0x30, 0x30, 0x30, 0x30, + 0x03, 0x03, 0x03, 0x03, 0x30, 0x30, 0x30, 0x30, + 0x03, 0x03, 0x03, 0x03, 0x30, 0x30, 0x30, 0x30, + 0x03, 0x03, 0x03, 0x03, 0x30, 0x30, 0x30, 0x30, + + // 177: + 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, 0xcc, 0xcc, 0xcc, 0xcc, + + // 178: + 0xf3, 0xf3, 0xf3, 0xf3, 0x3f, 0x3f, 0x3f, 0x3f, + 0xf3, 0xf3, 0xf3, 0xf3, 0x3f, 0x3f, 0x3f, 0x3f, + 0xf3, 0xf3, 0xf3, 0xf3, 0x3f, 0x3f, 0x3f, 0x3f, + 0xf3, 0xf3, 0xf3, 0xf3, 0x3f, 0x3f, 0x3f, 0x3f, + 0xf3, 0xf3, 0xf3, 0xf3, 0x3f, 0x3f, 0x3f, 0x3f, + 0xf3, 0xf3, 0xf3, 0xf3, 0x3f, 0x3f, 0x3f, 0x3f, + 0xf3, 0xf3, 0xf3, 0xf3, 0x3f, 0x3f, 0x3f, 0x3f, + 0xf3, 0xf3, 0xf3, 0xf3, 0x3f, 0x3f, 0x3f, 0x3f, + + // 179: + 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, + + // 180: + 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, 0xc0, + + // 181: + 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, 0xc0, + + // 182: + 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, 0xff, 0x3c, 0xff, 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, + + // 183: + 0x00, 0x00, 0x00, 0x00, 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, 0xfc, 0xff, 0xfc, + 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, + + // 184: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 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, 0xc0, + + // 185: + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0xff, 0x3c, 0xff, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0xff, 0x3c, 0xff, 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, + + // 186: + 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, 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, + + // 187: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xff, 0xfc, + 0x00, 0x3c, 0x00, 0x3c, 0xff, 0x3c, 0xff, 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, + + // 188: + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0xff, 0x3c, 0xff, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0xff, 0xfc, 0xff, 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, + + // 189: + 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, 0xff, 0xfc, 0xff, 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, + + // 190: + 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 191: + 0x00, 0x00, 0x00, 0x00, 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, 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, 0xc0, + + // 192: + 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 193: + 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, 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, + + // 194: + 0x00, 0x00, 0x00, 0x00, 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, + 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, + + // 195: + 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, + + // 196: + 0x00, 0x00, 0x00, 0x00, 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, + + // 197: + 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, 0xff, 0xff, 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, + + // 198: + 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, + + // 199: + 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, 0x3f, 0x0f, 0x3f, + 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, + + // 200: + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3f, 0x0f, 0x3f, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0xff, 0x0f, 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, + + // 201: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x3f, 0x0f, 0x3f, + 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, + + // 202: + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0xff, 0x3f, 0xff, 0x3f, + 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, + + // 203: + 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, 0x3f, 0xff, 0x3f, + 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, + + // 204: + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3f, 0x0f, 0x3f, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x3f, 0x0f, 0x3f, + 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, + + // 205: + 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, + + // 206: + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0xff, 0x3f, 0xff, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, + 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, + + // 207: + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 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, + + // 208: + 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, 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, + + // 209: + 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, + 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, + + // 210: + 0x00, 0x00, 0x00, 0x00, 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, 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, + + // 211: + 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, 0xff, 0x0f, 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, + + // 212: + 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 213: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 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, + + // 214: + 0x00, 0x00, 0x00, 0x00, 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, 0xff, 0x0f, 0xff, + 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, + + // 215: + 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, 0xff, 0xff, 0xff, 0xff, + 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, + + // 216: + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0xff, 0xff, 0xff, 0xff, + 0x03, 0xc0, 0x03, 0xc0, 0xff, 0xff, 0xff, 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, + + // 217: + 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 218: + 0x00, 0x00, 0x00, 0x00, 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, 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, + + // 219: + 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, + + // 220: + 0x00, 0x00, 0x00, 0x00, 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, + + // 221: + 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, + + // 222: + 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, + + // 223: + 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, + + // 224: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x7f, 0x7c, + 0xf3, 0xf8, 0xe1, 0xf0, 0xe1, 0xc0, 0xe1, 0xc0, + 0xe1, 0xc0, 0xe1, 0xc0, 0xe1, 0xc0, 0xe1, 0xc0, + 0xe1, 0xf0, 0xf3, 0xf8, 0x7f, 0xbc, 0x3f, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 225: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x80, 0x3f, 0xc0, 0x70, 0xe0, 0x70, 0xe0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xe0, 0xf1, 0xe0, + 0xf3, 0xc0, 0xf3, 0xc0, 0xf1, 0xe0, 0xf0, 0xf0, + 0xf0, 0x70, 0xf0, 0x38, 0xf0, 0x38, 0xf0, 0x38, + 0xf0, 0x38, 0xf0, 0x78, 0xf0, 0xf0, 0xf0, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 226: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xfc, 0xff, 0xfc, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 227: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xfc, 0xff, 0xfc, 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 228: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xff, 0xfc, + 0xf0, 0x1c, 0x78, 0x0c, 0x3c, 0x00, 0x1e, 0x00, + 0x0f, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x03, 0xc0, + 0x07, 0x80, 0x0f, 0x00, 0x1e, 0x00, 0x3c, 0x00, + 0x78, 0x0c, 0xf0, 0x1c, 0xff, 0xfc, 0xff, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 229: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0xfc, 0x3f, 0xfc, + 0x73, 0xc0, 0xe1, 0xc0, 0xe1, 0xc0, 0xe1, 0xc0, + 0xe1, 0xc0, 0xe1, 0xc0, 0xe1, 0xc0, 0xe1, 0xc0, + 0xe1, 0xc0, 0x73, 0x80, 0x3f, 0x00, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 230: + 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, 0x38, 0x3c, 0x78, 0x3f, 0xf0, 0x3f, 0xe0, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x38, 0x00, + 0xf0, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 231: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x1c, 0x3f, 0x3c, 0xf3, 0xf0, 0xe3, 0xe0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 232: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x3f, 0xfc, + 0x03, 0xc0, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, + 0x1c, 0x38, 0x38, 0x1c, 0x38, 0x1c, 0x38, 0x1c, + 0x38, 0x1c, 0x1c, 0x38, 0x0f, 0xf0, 0x07, 0xe0, + 0x03, 0xc0, 0x03, 0xc0, 0x3f, 0xfc, 0x3f, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 233: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x0f, 0xc0, + 0x1c, 0xe0, 0x38, 0x70, 0x70, 0x38, 0x70, 0x38, + 0xe0, 0x1c, 0xe0, 0x1c, 0xff, 0xfc, 0xff, 0xfc, + 0xe0, 0x1c, 0xe0, 0x1c, 0x70, 0x38, 0x70, 0x38, + 0x38, 0x70, 0x1c, 0xe0, 0x0f, 0xc0, 0x07, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 234: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0xc0, 0x1f, 0xe0, 0x3c, 0xf0, 0x78, 0x78, + 0xf0, 0x3c, 0xe0, 0x1c, 0xe0, 0x1c, 0xe0, 0x1c, + 0xe0, 0x1c, 0xe0, 0x1c, 0x70, 0x38, 0x78, 0x78, + 0x38, 0x70, 0x3c, 0xf0, 0x1c, 0xe0, 0x1c, 0xe0, + 0x1c, 0xe0, 0x1c, 0xe0, 0xfc, 0xfc, 0xfc, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 235: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x03, 0xfc, 0x07, 0x00, 0x07, 0x00, + 0x03, 0x80, 0x01, 0xc0, 0x00, 0xe0, 0x00, 0xf0, + 0x07, 0xf8, 0x0f, 0xfc, 0x1c, 0x3c, 0x38, 0x1c, + 0x38, 0x1c, 0x38, 0x1c, 0x38, 0x1c, 0x38, 0x1c, + 0x38, 0x1c, 0x1c, 0x38, 0x0f, 0xf0, 0x07, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 236: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x3f, 0xfc, + 0x73, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, + 0xf3, 0xcf, 0x73, 0xcf, 0x3f, 0xfc, 0x1f, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 237: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, + 0x00, 0x1c, 0x00, 0x38, 0x1f, 0xf8, 0x3f, 0xfc, + 0x70, 0xee, 0xf1, 0xcf, 0xf3, 0xcf, 0xf7, 0x8f, + 0xff, 0x0f, 0xff, 0x0e, 0x3f, 0xfc, 0x3f, 0xf8, + 0x38, 0x00, 0x30, 0x00, 0x70, 0x00, 0xe0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 238: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x03, 0xf0, 0x07, 0x80, 0x0f, 0x00, + 0x1e, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3f, 0xf0, 0x3f, 0xf0, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x1e, 0x00, + 0x0f, 0x00, 0x07, 0x80, 0x03, 0xf0, 0x00, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 239: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf0, 0x7f, 0xf8, + 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, + + // 240: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xfc, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xff, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xfc, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 241: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, + 0x3f, 0xfc, 0x3f, 0xfc, 0x03, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 0xc0, 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, + + // 242: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x0f, 0x00, + 0x07, 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0xf0, + 0x00, 0x78, 0x00, 0x78, 0x00, 0xf0, 0x01, 0xe0, + 0x03, 0xc0, 0x07, 0x80, 0x0f, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x3f, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 243: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xf0, + 0x01, 0xe0, 0x03, 0xc0, 0x07, 0x80, 0x0f, 0x00, + 0x1e, 0x00, 0x1e, 0x00, 0x0f, 0x00, 0x07, 0x80, + 0x03, 0xc0, 0x01, 0xe0, 0x00, 0xf0, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x3f, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 244: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x01, 0xfe, 0x03, 0xcf, 0x03, 0xcf, + 0x03, 0xcf, 0x03, 0xcf, 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, + + // 245: + 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, + 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, + 0xf3, 0xc0, 0xf3, 0xc0, 0x7f, 0x80, 0x3f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 246: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x80, 0x03, 0xc0, 0x03, 0xc0, 0x01, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x3f, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0xc0, + 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 247: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x1c, 0x3f, 0x3c, + 0xf3, 0xf0, 0xe1, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x1c, 0x3f, 0x3c, 0xf3, 0xf0, 0xe1, 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, + + // 248: + 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x0f, 0xc0, + 0x1c, 0xe0, 0x38, 0x70, 0x38, 0x70, 0x1c, 0xe0, + 0x0f, 0xc0, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 249: + 0x00, 0x00, 0x00, 0x00, 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, 0xc0, 0x03, 0xc0, + 0x03, 0xc0, 0x03, 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, + + // 250: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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, 0xc0, 0x03, 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, + + // 251: + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, + 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, + 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, + 0x00, 0xf0, 0x00, 0xf0, 0xe0, 0xf0, 0xf0, 0xf0, + 0x78, 0xf0, 0x3c, 0xf0, 0x1e, 0xf0, 0x0f, 0xf0, + 0x07, 0xf0, 0x03, 0xf0, 0x01, 0xf0, 0x00, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 252: + 0x00, 0x00, 0x00, 0x00, 0xf3, 0xc0, 0xf7, 0xe0, + 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, + 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, + 0x3c, 0xf0, 0x3c, 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, + + // 253: + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x3f, 0x00, + 0x73, 0x80, 0xe3, 0xc0, 0x07, 0x00, 0x0e, 0x00, + 0x1c, 0x00, 0x38, 0x00, 0x70, 0xc0, 0xe0, 0xc0, + 0xff, 0xc0, 0xff, 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, + + // 254: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0xf0, 0x3f, 0xf0, 0x3f, 0xf0, 0x3f, 0xf0, + 0x3f, 0xf0, 0x3f, 0xf0, 0x3f, 0xf0, 0x3f, 0xf0, + 0x3f, 0xf0, 0x3f, 0xf0, 0x3f, 0xf0, 0x3f, 0xf0, + 0x3f, 0xf0, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 255: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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 txt_font_t large_font = +{ + large_font_data, + 16, // width + 32 // height +}; + diff --git a/src/TEXTSCREEN/txt_main.h b/src/TEXTSCREEN/txt_main.h new file mode 100644 index 0000000..02a7cdd --- /dev/null +++ b/src/TEXTSCREEN/txt_main.h @@ -0,0 +1,154 @@ +// +// 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 + +// For the moment, txt_sdl.c is the only implementation of the base +// text mode screen API: + +#include "txt_sdl.h" + +// 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_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 ) + +// Screen size + +#define TXT_SCREEN_W 80 +#define TXT_SCREEN_H 25 + +#define TXT_COLOR_BLINKING (1 << 3) + +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; + +// 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_GetScreenData(void); + +// Update an area of the screen +void TXT_UpdateScreenArea(int x, int y, int w, int h); + +// Update the whole screen +void TXT_UpdateScreen(void); + +// Read a character from the keyboard +int TXT_GetChar(void); + +// Read the current state of modifier keys that are held down. +int TXT_GetModifierState(txt_modifier_t mod); + +// Provides a short description of a key code, placing into the +// provided buffer. +void TXT_GetKeyDescription(int key, char *buf, size_t buf_len); + +// Retrieve the current position of the mouse +void TXT_GetMousePosition(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); + +// Controls whether keys are returned from TXT_GetChar based on keyboard +// mapping, or raw key code. +void TXT_EnableKeyMapping(int enable); + +// Set the window title of the window containing the text mode screen +void TXT_SetWindowTitle(char *title); + +// Safe string copy. +void TXT_StringCopy(char *dest, const char *src, size_t dest_len); + +// Safe string concatenate. +void TXT_StringConcat(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, ...); + +#endif /* #ifndef TXT_MAIN_H */ + diff --git a/src/TEXTSCREEN/txt_sdl.c b/src/TEXTSCREEN/txt_sdl.c new file mode 100644 index 0000000..79efb0e --- /dev/null +++ b/src/TEXTSCREEN/txt_sdl.c @@ -0,0 +1,945 @@ +// +// 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 +// + +#include "SDL.h" + +#include +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_main.h" +#include "txt_sdl.h" + +#if defined(_MSC_VER) && !defined(__cplusplus) +#define inline __inline +#endif + +typedef struct +{ + unsigned char *data; + unsigned int w; + unsigned int h; +} txt_font_t; + +// Fonts: + +#include "txt_font.h" +#include "txt_largefont.h" +#include "txt_smallfont.h" + +// Time between character blinks in ms + +#define BLINK_PERIOD 250 + +SDL_Window *TXT_SDLWindow; +static SDL_Surface *screenbuffer; +static unsigned char *screendata; +static int key_mapping = 1; + +static TxtSDLEventCallbackFunc event_callback; +static void *event_callback_data; + +static int modifier_state[TXT_NUM_MODIFIERS]; + +// Font we are using: + +static txt_font_t *font; + +//#define TANGO + +#ifndef TANGO + +static SDL_Color ega_colors[] = +{ + {0x00, 0x00, 0x00, 0xff}, // 0: Black + {0x00, 0x00, 0xa8, 0xff}, // 1: Blue + {0x00, 0xa8, 0x00, 0xff}, // 2: Green + {0x00, 0xa8, 0xa8, 0xff}, // 3: Cyan + {0xa8, 0x00, 0x00, 0xff}, // 4: Red + {0xa8, 0x00, 0xa8, 0xff}, // 5: Magenta + {0xa8, 0x54, 0x00, 0xff}, // 6: Brown + {0xa8, 0xa8, 0xa8, 0xff}, // 7: Grey + {0x54, 0x54, 0x54, 0xff}, // 8: Dark grey + {0x54, 0x54, 0xfe, 0xff}, // 9: Bright blue + {0x54, 0xfe, 0x54, 0xff}, // 10: Bright green + {0x54, 0xfe, 0xfe, 0xff}, // 11: Bright cyan + {0xfe, 0x54, 0x54, 0xff}, // 12: Bright red + {0xfe, 0x54, 0xfe, 0xff}, // 13: Bright magenta + {0xfe, 0xfe, 0x54, 0xff}, // 14: Yellow + {0xfe, 0xfe, 0xfe, 0xff}, // 15: Bright white +}; + +#else + +// Colors that fit the Tango desktop guidelines: see +// http://tango.freedesktop.org/ also +// http://uwstopia.nl/blog/2006/07/tango-terminal + +static SDL_Color ega_colors[] = +{ + {0x2e, 0x34, 0x36, 0xff}, // 0: Black + {0x34, 0x65, 0xa4, 0xff}, // 1: Blue + {0x4e, 0x9a, 0x06, 0xff}, // 2: Green + {0x06, 0x98, 0x9a, 0xff}, // 3: Cyan + {0xcc, 0x00, 0x00, 0xff}, // 4: Red + {0x75, 0x50, 0x7b, 0xff}, // 5: Magenta + {0xc4, 0xa0, 0x00, 0xff}, // 6: Brown + {0xd3, 0xd7, 0xcf, 0xff}, // 7: Grey + {0x55, 0x57, 0x53, 0xff}, // 8: Dark grey + {0x72, 0x9f, 0xcf, 0xff}, // 9: Bright blue + {0x8a, 0xe2, 0x34, 0xff}, // 10: Bright green + {0x34, 0xe2, 0xe2, 0xff}, // 11: Bright cyan + {0xef, 0x29, 0x29, 0xff}, // 12: Bright red + {0x34, 0xe2, 0xe2, 0xff}, // 13: Bright magenta + {0xfc, 0xe9, 0x4f, 0xff}, // 14: Yellow + {0xee, 0xee, 0xec, 0xff}, // 15: Bright white +}; + +#endif + +#ifdef _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include + +// Examine system DPI settings to determine whether to use the large font. + +static int Win32_UseLargeFont(void) +{ + HDC hdc = GetDC(NULL); + int dpix; + + if (!hdc) + { + return 0; + } + + dpix = GetDeviceCaps(hdc, LOGPIXELSX); + ReleaseDC(NULL, hdc); + + // 144 is the DPI when using "150%" scaling. If the user has this set + // then consider this an appropriate threshold for using the large font. + + return dpix >= 144; +} + +#endif + +static txt_font_t *FontForName(char *name) +{ + if (!strcmp(name, "small")) + { + return &small_font; + } + else if (!strcmp(name, "normal")) + { + return &main_font; + } + else if (!strcmp(name, "large")) + { + return &large_font; + } + else + { + return NULL; + } +} + +// +// Select the font to use, based on screen resolution +// +// If the highest screen resolution available is less than +// 640x480, use the small font. +// + +static void ChooseFont(void) +{ + SDL_DisplayMode desktop_info; + char *env; + + // Allow normal selection to be overridden from an environment variable: + + env = getenv("TEXTSCREEN_FONT"); + + if (env != NULL) + { + font = FontForName(env); + + if (font != NULL) + { + return; + } + } + + // Get desktop resolution. + // If in doubt and we can't get a list, always prefer to + // fall back to the normal font: + + if (!SDL_GetCurrentDisplayMode(0, &desktop_info)) + { + font = &main_font; + return; + } + + // On tiny low-res screens (eg. palmtops) use the small font. + // If the screen resolution is at least 1920x1080, this is + // a modern high-resolution display, and we can use the + // large font. + + if (desktop_info.w < 640 || desktop_info.h < 480) + { + font = &small_font; + } +#ifdef _WIN32 + // On Windows we can use the system DPI settings to make a + // more educated guess about whether to use the large font. + + else if (Win32_UseLargeFont()) + { + font = &large_font; + } +#endif + // TODO: Detect high DPI on Linux by inquiring about Gtk+ scale + // settings. This looks like it should just be a case of shelling + // out to invoke the 'gsettings' command, eg. + // gsettings get org.gnome.desktop.interface text-scaling-factor + // and using large_font if the result is >= 2. + else + { + font = &main_font; + } +} + +// +// Initialize text mode screen +// +// Returns 1 if successful, 0 if an error occurred +// + +int TXT_Init(void) +{ + if (SDL_Init(SDL_INIT_VIDEO) < 0) + { + return 0; + } + + ChooseFont(); + + // Always create the screen at the native screen depth (bpp=0); + // some systems nowadays don't seem to support true 8-bit palettized + // screen modes very well and we end up with screwed up colors. + TXT_SDLWindow = + SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + TXT_SCREEN_W * font->w, TXT_SCREEN_H * font->h, + 0); + + if (TXT_SDLWindow == NULL) + return 0; + + // Instead, we draw everything into an intermediate 8-bit surface + // the same dimensions as the screen. SDL then takes care of all the + // 8->32 bit (or whatever depth) color conversions for us. + screenbuffer = SDL_CreateRGBSurface(0, + TXT_SCREEN_W * font->w, + TXT_SCREEN_H * font->h, + 8, 0, 0, 0, 0); + + SDL_LockSurface(screenbuffer); + SDL_SetPaletteColors(screenbuffer->format->palette, ega_colors, 0, 16); + SDL_UnlockSurface(screenbuffer); + // SDL2-TODO SDL_EnableUNICODE(1); + + screendata = (unsigned char *)malloc(TXT_SCREEN_W * TXT_SCREEN_H * 2); + memset(screendata, 0, TXT_SCREEN_W * TXT_SCREEN_H * 2); + + // Ignore all mouse motion events +// SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE); + + // Repeat key presses so we can hold down arrows to scroll down the + // menu, for example. This is what setup.exe does. + + // SDL2-TODO SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); + + return 1; +} + +void TXT_Shutdown(void) +{ + free(screendata); + screendata = NULL; + SDL_FreeSurface(screenbuffer); + screenbuffer = NULL; + SDL_QuitSubSystem(SDL_INIT_VIDEO); +} + +unsigned char *TXT_GetScreenData(void) +{ + return screendata; +} + +static inline void UpdateCharacter(int x, int y) +{ + unsigned char character; + unsigned char *p; + unsigned char *s, *s1; + unsigned int bit, bytes; + int bg, fg; + unsigned int x1, y1; + + p = &screendata[(y * TXT_SCREEN_W + x) * 2]; + character = p[0]; + + fg = p[1] & 0xf; + bg = (p[1] >> 4) & 0xf; + + if (bg & 0x8) + { + // blinking + + bg &= ~0x8; + + if (((SDL_GetTicks() / BLINK_PERIOD) % 2) == 0) + { + fg = bg; + } + } + + // How many bytes per line? + bytes = (font->w + 7) / 8; + p = &font->data[character * font->h * bytes]; + + s = ((unsigned char *) screenbuffer->pixels) + + (y * font->h * screenbuffer->pitch) + + (x * font->w); + + for (y1=0; y1h; ++y1) + { + s1 = s; + bit = 0; + + for (x1=0; x1w; ++x1) + { + if (*p & (1 << (7-bit))) + { + *s1++ = fg; + } + else + { + *s1++ = bg; + } + + ++bit; + if (bit == 8) + { + ++p; + bit = 0; + } + } + + if (bit != 0) + { + ++p; + } + + s += screenbuffer->pitch; + } +} + +static int LimitToRange(int val, int min, int max) +{ + if (val < min) + { + return min; + } + else if (val > max) + { + return max; + } + else + { + return val; + } +} + +void TXT_UpdateScreenArea(int x, int y, int w, int h) +{ + SDL_Rect rect; + int x1, y1; + int x_end; + int y_end; + + SDL_LockSurface(screenbuffer); + + x_end = LimitToRange(x + w, 0, TXT_SCREEN_W); + y_end = LimitToRange(y + h, 0, TXT_SCREEN_H); + x = LimitToRange(x, 0, TXT_SCREEN_W); + y = LimitToRange(y, 0, TXT_SCREEN_H); + + for (y1=y; y1w; + rect.y = y * font->h; + rect.w = (x_end - x) * font->w; + rect.h = (y_end - y) * font->h; + + SDL_UnlockSurface(screenbuffer); + + SDL_BlitSurface(screenbuffer, &rect, + SDL_GetWindowSurface(TXT_SDLWindow), &rect); + SDL_UpdateWindowSurfaceRects(TXT_SDLWindow, &rect, 1); +} + +void TXT_UpdateScreen(void) +{ + TXT_UpdateScreenArea(0, 0, TXT_SCREEN_W, TXT_SCREEN_H); +} + +void TXT_GetMousePosition(int *x, int *y) +{ + SDL_GetMouseState(x, y); + + *x /= font->w; + *y /= font->h; +} + +// +// Translates the SDL key +// + +static int TranslateKey(SDL_Keysym *sym) +{ + switch(sym->sym) + { + case SDLK_LEFT: return KEY_LEFTARROW; + case SDLK_RIGHT: return KEY_RIGHTARROW; + case SDLK_DOWN: return KEY_DOWNARROW; + case SDLK_UP: return KEY_UPARROW; + case SDLK_ESCAPE: return KEY_ESCAPE; + case SDLK_RETURN: return KEY_ENTER; + case SDLK_TAB: return KEY_TAB; + case SDLK_F1: return KEY_F1; + case SDLK_F2: return KEY_F2; + case SDLK_F3: return KEY_F3; + case SDLK_F4: return KEY_F4; + case SDLK_F5: return KEY_F5; + case SDLK_F6: return KEY_F6; + case SDLK_F7: return KEY_F7; + case SDLK_F8: return KEY_F8; + case SDLK_F9: return KEY_F9; + case SDLK_F10: return KEY_F10; + case SDLK_F11: return KEY_F11; + case SDLK_F12: return KEY_F12; + case SDLK_PRINTSCREEN: return KEY_PRTSCR; + + case SDLK_BACKSPACE: return KEY_BACKSPACE; + case SDLK_DELETE: return KEY_DEL; + + case SDLK_PAUSE: return KEY_PAUSE; + + case SDLK_LSHIFT: + case SDLK_RSHIFT: + return KEY_RSHIFT; + + case SDLK_LCTRL: + case SDLK_RCTRL: + return KEY_RCTRL; + + case SDLK_LALT: + case SDLK_RALT: + return KEY_RALT; + + case SDLK_CAPSLOCK: return KEY_CAPSLOCK; + case SDLK_SCROLLLOCK: return KEY_SCRLCK; + + case SDLK_HOME: return KEY_HOME; + case SDLK_INSERT: return KEY_INS; + case SDLK_END: return KEY_END; + case SDLK_PAGEUP: return KEY_PGUP; + case SDLK_PAGEDOWN: return KEY_PGDN; + +#ifdef SDL_HAVE_APP_KEYS + case SDLK_APP1: return KEY_F1; + case SDLK_APP2: return KEY_F2; + case SDLK_APP3: return KEY_F3; + case SDLK_APP4: return KEY_F4; + case SDLK_APP5: return KEY_F5; + case SDLK_APP6: return KEY_F6; +#endif + + default: break; + } + + // Returned value is different, depending on whether key mapping is + // enabled. Key mapping is preferable most of the time, for typing + // in text, etc. However, when we want to read raw keyboard codes + // for the setup keyboard configuration dialog, we want the raw + // key code. + + if (key_mapping) + { + // Unicode characters beyond the ASCII range need to be + // mapped up into textscreen's Unicode range. + +#if 0 + // SDL2-TODO + if (sym->unicode < 128) + { + return sym->unicode; + } + else + { + return sym->unicode - 128 + TXT_UNICODE_BASE; + } +#endif + return 0; + } + else + { + // Keypad mapping is only done when we want a raw value: + // most of the time, the keypad should behave as it normally + // does. + + switch (sym->sym) + { + case SDLK_KP_0: return KEYP_0; + case SDLK_KP_1: return KEYP_1; + case SDLK_KP_2: return KEYP_2; + case SDLK_KP_3: return KEYP_3; + case SDLK_KP_4: return KEYP_4; + case SDLK_KP_5: return KEYP_5; + case SDLK_KP_6: return KEYP_6; + case SDLK_KP_7: return KEYP_7; + case SDLK_KP_8: return KEYP_8; + case SDLK_KP_9: return KEYP_9; + + case SDLK_KP_PERIOD: return KEYP_PERIOD; + case SDLK_KP_MULTIPLY: return KEYP_MULTIPLY; + case SDLK_KP_PLUS: return KEYP_PLUS; + case SDLK_KP_MINUS: return KEYP_MINUS; + case SDLK_KP_DIVIDE: return KEYP_DIVIDE; + case SDLK_KP_EQUALS: return KEYP_EQUALS; + case SDLK_KP_ENTER: return KEYP_ENTER; + + default: + return tolower(sym->sym); + } + } +} + +// Convert an SDL button index to textscreen button index. +// +// Note special cases because 2 == mid in SDL, 3 == mid in textscreen/setup + +static int SDLButtonToTXTButton(int button) +{ + switch (button) + { + case SDL_BUTTON_LEFT: + return TXT_MOUSE_LEFT; + case SDL_BUTTON_RIGHT: + return TXT_MOUSE_RIGHT; + case SDL_BUTTON_MIDDLE: + return TXT_MOUSE_MIDDLE; + default: + return TXT_MOUSE_BASE + button - 1; + } +} + +static int MouseHasMoved(void) +{ + static int last_x = 0, last_y = 0; + int x, y; + + TXT_GetMousePosition(&x, &y); + + if (x != last_x || y != last_y) + { + last_x = x; last_y = y; + return 1; + } + else + { + return 0; + } +} + +// Examine a key press/release and update the modifier key state +// if necessary. + +static void UpdateModifierState(SDL_Keysym *sym, int pressed) +{ + txt_modifier_t mod; + + switch (sym->sym) + { + case SDLK_LSHIFT: + case SDLK_RSHIFT: + mod = TXT_MOD_SHIFT; + break; + + case SDLK_LCTRL: + case SDLK_RCTRL: + mod = TXT_MOD_CTRL; + break; + + case SDLK_LALT: + case SDLK_RALT: + mod = TXT_MOD_ALT; + break; + + default: + return; + } + + if (pressed) + { + ++modifier_state[mod]; + } + else + { + --modifier_state[mod]; + } +} + +signed int TXT_GetChar(void) +{ + SDL_Event ev; + + while (SDL_PollEvent(&ev)) + { + // If there is an event callback, allow it to intercept this + // event. + + if (event_callback != NULL) + { + if (event_callback(&ev, event_callback_data)) + { + continue; + } + } + + // Process the event. + + switch (ev.type) + { + case SDL_MOUSEBUTTONDOWN: + if (ev.button.button < TXT_MAX_MOUSE_BUTTONS) + { + return SDLButtonToTXTButton(ev.button.button); + } + break; + + case SDL_KEYDOWN: + UpdateModifierState(&ev.key.keysym, 1); + + return TranslateKey(&ev.key.keysym); + + case SDL_KEYUP: + UpdateModifierState(&ev.key.keysym, 0); + break; + + case SDL_QUIT: + // Quit = escape + return 27; + + case SDL_MOUSEMOTION: + if (MouseHasMoved()) + { + return 0; + } + + default: + break; + } + } + + return -1; +} + +int TXT_GetModifierState(txt_modifier_t mod) +{ + if (mod < TXT_NUM_MODIFIERS) + { + return modifier_state[mod] > 0; + } + + return 0; +} + +static const char *SpecialKeyName(int key) +{ + switch (key) + { + case ' ': return "SPACE"; + case KEY_RIGHTARROW: return "RIGHT"; + case KEY_LEFTARROW: return "LEFT"; + case KEY_UPARROW: return "UP"; + case KEY_DOWNARROW: return "DOWN"; + case KEY_ESCAPE: return "ESC"; + case KEY_ENTER: return "ENTER"; + case KEY_TAB: return "TAB"; + case KEY_F1: return "F1"; + case KEY_F2: return "F2"; + case KEY_F3: return "F3"; + case KEY_F4: return "F4"; + case KEY_F5: return "F5"; + case KEY_F6: return "F6"; + case KEY_F7: return "F7"; + case KEY_F8: return "F8"; + case KEY_F9: return "F9"; + case KEY_F10: return "F10"; + case KEY_F11: return "F11"; + case KEY_F12: return "F12"; + case KEY_BACKSPACE: return "BKSP"; + case KEY_PAUSE: return "PAUSE"; + case KEY_EQUALS: return "EQUALS"; + case KEY_MINUS: return "MINUS"; + case KEY_RSHIFT: return "SHIFT"; + case KEY_RCTRL: return "CTRL"; + case KEY_RALT: return "ALT"; + case KEY_CAPSLOCK: return "CAPS"; + case KEY_SCRLCK: return "SCRLCK"; + case KEY_HOME: return "HOME"; + case KEY_END: return "END"; + case KEY_PGUP: return "PGUP"; + case KEY_PGDN: return "PGDN"; + case KEY_INS: return "INS"; + case KEY_DEL: return "DEL"; + case KEY_PRTSCR: return "PRTSC"; + /* + case KEYP_0: return "PAD0"; + case KEYP_1: return "PAD1"; + case KEYP_2: return "PAD2"; + case KEYP_3: return "PAD3"; + case KEYP_4: return "PAD4"; + case KEYP_5: return "PAD5"; + case KEYP_6: return "PAD6"; + case KEYP_7: return "PAD7"; + case KEYP_8: return "PAD8"; + case KEYP_9: return "PAD9"; + case KEYP_UPARROW: return "PAD_U"; + case KEYP_DOWNARROW: return "PAD_D"; + case KEYP_LEFTARROW: return "PAD_L"; + case KEYP_RIGHTARROW: return "PAD_R"; + case KEYP_MULTIPLY: return "PAD*"; + case KEYP_PLUS: return "PAD+"; + case KEYP_MINUS: return "PAD-"; + case KEYP_DIVIDE: return "PAD/"; + */ + + default: return NULL; + } +} + +void TXT_GetKeyDescription(int key, char *buf, size_t buf_len) +{ + const char *keyname; + + keyname = SpecialKeyName(key); + + if (keyname != NULL) + { + TXT_StringCopy(buf, keyname, buf_len); + } + else if (isprint(key)) + { + TXT_snprintf(buf, buf_len, "%c", toupper(key)); + } + else + { + TXT_snprintf(buf, buf_len, "??%i", key); + } +} + +// Searches the desktop screen buffer to determine whether there are any +// blinking characters. + +int TXT_ScreenHasBlinkingChars(void) +{ + int x, y; + unsigned char *p; + + // Check all characters in screen buffer + + for (y=0; y time_to_next_blink) + { + // Add one so it is always positive + + timeout = time_to_next_blink + 1; + } + } + + if (timeout == 0) + { + // We can just wait forever until an event occurs + + SDL_WaitEvent(NULL); + } + else + { + // Sit in a busy loop until the timeout expires or we have to + // redraw the blinking screen + + start_time = SDL_GetTicks(); + + while (SDL_GetTicks() < start_time + timeout) + { + if (SDL_PollEvent(NULL) != 0) + { + // Received an event, so stop waiting + + break; + } + + // Don't hog the CPU + + SDL_Delay(1); + } + } +} + +void TXT_EnableKeyMapping(int enable) +{ + key_mapping = enable; +} + +void TXT_SetWindowTitle(char *title) +{ + SDL_SetWindowTitle(TXT_SDLWindow, title); +} + +void TXT_SDL_SetEventCallback(TxtSDLEventCallbackFunc callback, void *user_data) +{ + event_callback = callback; + event_callback_data = user_data; +} + +// Safe string functions. + +void TXT_StringCopy(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); +} + +void TXT_StringConcat(char *dest, const char *src, size_t dest_len) +{ + size_t offset; + + offset = strlen(dest); + if (offset > dest_len) + { + offset = dest_len; + } + + TXT_StringCopy(dest + offset, src, dest_len - offset); +} + +// On Windows, vsnprintf() is _vsnprintf(). +#ifdef _WIN32 +#if _MSC_VER < 1400 /* not needed for Visual Studio 2008 */ +#define vsnprintf _vsnprintf +#endif +#endif + +// 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 || (size_t)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/src/TEXTSCREEN/txt_sdl.h b/src/TEXTSCREEN/txt_sdl.h new file mode 100644 index 0000000..629dc58 --- /dev/null +++ b/src/TEXTSCREEN/txt_sdl.h @@ -0,0 +1,41 @@ +// +// 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 + +// The textscreen API itself doesn't need SDL; however, SDL needs its +// headers included where main() is defined. + +#include "SDL.h" + +// Event callback function type: a function of this type can be used +// to intercept events in the textscreen event processing loop. +// Returning 1 will cause the event to be eaten; the textscreen code +// will not see it. + +typedef int (*TxtSDLEventCallbackFunc)(SDL_Event *event, void *user_data); + +// 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_SetEventCallback(TxtSDLEventCallbackFunc callback, void *user_data); + +#endif /* #ifndef TXT_SDL_H */ + diff --git a/src/TEXTSCREEN/txt_smallfont.h b/src/TEXTSCREEN/txt_smallfont.h new file mode 100644 index 0000000..e152a4c --- /dev/null +++ b/src/TEXTSCREEN/txt_smallfont.h @@ -0,0 +1,2876 @@ +// +// 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/ +// +// ---- +// +// Copyright (C) 2005-2014 Simon Howard +// Copyright (C) 2002-2004 The DOSBox Team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// +// Small (4x8) bitmap font for low resolution displays. +// +// 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 (txt_font.h) +// + +static unsigned char small_font_data[] = { + + // ------ Characters 0-31 have been remade to match the ------ + // DOS control code ASCII characters. + + // Character 0: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 1: + + 0x00, // | | + 0x00, // | | + 0x50, // | # #| + 0x00, // | | + 0x70, // | ###| + 0x20, // | # | + 0x00, // | | + 0x00, // | | + + // Character 2: + + 0x60, // | ## | + 0xf0, // |####| + 0xa0, // |# # | + 0xf0, // |####| + 0x80, // |# | + 0xd0, // |## #| + 0xf0, // |####| + 0x60, // | ## | + + // Character 3: + + 0x00, // | | + 0x00, // | | + 0x50, // | # #| + 0x70, // | ###| + 0x20, // | # | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 4: + + 0x00, // | | + 0x00, // | | + 0x20, // | # | + 0x70, // | ###| + 0x20, // | # | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 5: + + 0x00, // | | + 0x20, // | # | + 0x50, // | # #| + 0x50, // | # #| + 0x20, // | # | + 0x70, // | ###| + 0x00, // | | + 0x00, // | | + + // Character 6: + + 0x00, // | | + 0x20, // | # | + 0x70, // | ###| + 0x70, // | ###| + 0x20, // | # | + 0x70, // | ###| + 0x00, // | | + 0x00, // | | + + // Character 7: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x60, // | ## | + 0x60, // | ## | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 8: + + 0xf0, // |####| + 0xf0, // |####| + 0xf0, // |####| + 0x90, // |# #| + 0x90, // |# #| + 0xf0, // |####| + 0xf0, // |####| + 0xf0, // |####| + + // Character 9: + + 0x00, // | | + 0x00, // | | + 0x60, // | ## | + 0x90, // |# #| + 0x90, // |# #| + 0x60, // | ## | + 0x00, // | | + 0x00, // | | + + // Character 10: + + 0xf0, // |####| + 0xf0, // |####| + 0x90, // |# #| + 0x60, // | ## | + 0x60, // | ## | + 0x90, // |# #| + 0xf0, // |####| + 0xf0, // |####| + + // Character 11: + + 0x00, // | | + 0x70, // | ###| + 0x20, // | # | + 0xe0, // |### | + 0xa0, // |# # | + 0xe0, // |### | + 0x00, // | | + 0x00, // | | + + // Character 12: + + 0x00, // | | + 0x60, // | ## | + 0x90, // |# #| + 0x60, // | ## | + 0xf0, // |####| + 0x60, // | ## | + 0x60, // | ## | + 0x00, // | | + + // Character 13: + + 0x00, // | | + 0x30, // | ##| + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0xe0, // |### | + 0xc0, // |## | + 0x00, // | | + + // Character 14: + + 0x00, // | | + 0x70, // | ###| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0xd0, // |## #| + 0xc0, // |## | + 0x00, // | | + + // Character 15: + + 0x40, // | # | + 0x40, // | # | + 0xe0, // |### | + 0x40, // | # | + 0xe0, // |### | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + + // Character 16: + + 0x00, // | | + 0x80, // |# | + 0xc0, // |## | + 0xe0, // |### | + 0xc0, // |## | + 0x80, // |# | + 0x00, // | | + 0x00, // | | + + // Character 17: + + 0x00, // | | + 0x10, // | #| + 0x30, // | ##| + 0x70, // | ###| + 0x30, // | ##| + 0x10, // | #| + 0x00, // | | + 0x00, // | | + + // Character 18: + + 0x00, // | | + 0x20, // | # | + 0x70, // | ###| + 0x20, // | # | + 0x20, // | # | + 0x70, // | ###| + 0x20, // | # | + 0x00, // | | + + // Character 19: + + 0x00, // | | + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x00, // | | + 0x50, // | # #| + 0x00, // | | + 0x00, // | | + + // Character 20: + + 0x00, // | | + 0xf0, // |####| + 0x90, // |# #| + 0xd0, // |## #| + 0x50, // | # #| + 0x50, // | # #| + 0x00, // | | + 0x00, // | | + + // Character 21: + + 0x60, // | ## | + 0x80, // |# | + 0x60, // | ## | + 0x90, // |# #| + 0x60, // | ## | + 0x10, // | #| + 0x60, // | ## | + 0x00, // | | + + // Character 22: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0xf0, // |####| + 0xf0, // |####| + 0x00, // | | + 0x00, // | | + + // Character 23: + + 0x00, // | | + 0x20, // | # | + 0x70, // | ###| + 0x20, // | # | + 0x20, // | # | + 0x70, // | ###| + 0x20, // | # | + 0xf0, // |####| + + // Character 24: + + 0x00, // | | + 0x20, // | # | + 0x70, // | ###| + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x00, // | | + + // Character 25: + + 0x00, // | | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x70, // | ###| + 0x20, // | # | + 0x00, // | | + + // Character 26: + + 0x00, // | | + 0x40, // | # | + 0x20, // | # | + 0xf0, // |####| + 0x20, // | # | + 0x40, // | # | + 0x00, // | | + 0x00, // | | + + // Character 27: + + 0x00, // | | + 0x20, // | # | + 0x40, // | # | + 0xf0, // |####| + 0x40, // | # | + 0x20, // | # | + 0x00, // | | + 0x00, // | | + + // Character 28: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x80, // |# | + 0xe0, // |### | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 29: + + 0x00, // | | + 0x00, // | | + 0xa0, // |# # | + 0xf0, // |####| + 0xa0, // |# # | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 30: + + 0x00, // | | + 0x00, // | | + 0x40, // | # | + 0xe0, // |### | + 0xf0, // |####| + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 31: + + 0x00, // | | + 0x00, // | | + 0xf0, // |####| + 0xe0, // |### | + 0x40, // | # | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // ------ Characters 32-127 are from Atari-Small ------ + + // Character 32: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 33: + + 0x00, // | | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x00, // | | + 0x40, // | # | + 0x00, // | | + + // Character 34: + + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 35: + + 0x00, // | | + 0xa0, // |# # | + 0xe0, // |### | + 0xa0, // |# # | + 0xa0, // |# # | + 0xe0, // |### | + 0xa0, // |# # | + 0x00, // | | + + // Character 36: + + 0x40, // | # | + 0x40, // | # | + 0xa0, // |# # | + 0x40, // | # | + 0x20, // | # | + 0xa0, // |# # | + 0x40, // | # | + 0x40, // | # | + + // Character 37: + + 0x00, // | | + 0xa0, // |# # | + 0x20, // | # | + 0x40, // | # | + 0x40, // | # | + 0x80, // |# | + 0xa0, // |# # | + 0x00, // | | + + // Character 38: + + 0x40, // | # | + 0xa0, // |# # | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xc0, // |## | + 0x60, // | ## | + 0x00, // | | + + // Character 39: + + 0x00, // | | + 0x40, // | # | + 0x40, // | # | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 40: + + 0x00, // | | + 0x20, // | # | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x20, // | # | + 0x00, // | | + + // Character 41: + + 0x00, // | | + 0x80, // |# | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x80, // |# | + 0x00, // | | + + // Character 42: + + 0x00, // | | + 0xa0, // |# # | + 0x40, // | # | + 0xe0, // |### | + 0x40, // | # | + 0xa0, // |# # | + 0x00, // | | + 0x00, // | | + + // Character 43: + + 0x00, // | | + 0x40, // | # | + 0x40, // | # | + 0xe0, // |### | + 0x40, // | # | + 0x40, // | # | + 0x00, // | | + 0x00, // | | + + // Character 44: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x40, // | # | + 0x40, // | # | + 0x80, // |# | + + // Character 45: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0xe0, // |### | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 46: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x40, // | # | + 0x40, // | # | + 0x00, // | | + + // Character 47: + + 0x00, // | | + 0x20, // | # | + 0x20, // | # | + 0x40, // | # | + 0x40, // | # | + 0x80, // |# | + 0x80, // |# | + 0x00, // | | + + // Character 48: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0xe0, // |### | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 49: + + 0x00, // | | + 0x40, // | # | + 0xc0, // |## | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0xe0, // |### | + 0x00, // | | + + // Character 50: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0x20, // | # | + 0x40, // | # | + 0x80, // |# | + 0xe0, // |### | + 0x00, // | | + + // Character 51: + + 0x00, // | | + 0xe0, // |### | + 0x20, // | # | + 0x40, // | # | + 0x20, // | # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 52: + + 0x00, // | | + 0x20, // | # | + 0x60, // | ## | + 0xa0, // |# # | + 0xe0, // |### | + 0x20, // | # | + 0x20, // | # | + 0x00, // | | + + // Character 53: + + 0x00, // | | + 0xe0, // |### | + 0x80, // |# | + 0xc0, // |## | + 0x20, // | # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 54: + + 0x00, // | | + 0x60, // | ## | + 0x80, // |# | + 0xc0, // |## | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 55: + + 0x00, // | | + 0xe0, // |### | + 0x20, // | # | + 0x20, // | # | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x00, // | | + + // Character 56: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 57: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x60, // | ## | + 0x20, // | # | + 0xc0, // |## | + 0x00, // | | + + // Character 58: + + 0x00, // | | + 0x00, // | | + 0x40, // | # | + 0x00, // | | + 0x00, // | | + 0x40, // | # | + 0x00, // | | + 0x00, // | | + + // Character 59: + + 0x00, // | | + 0x00, // | | + 0x40, // | # | + 0x00, // | | + 0x00, // | | + 0x40, // | # | + 0x80, // |# | + 0x00, // | | + + // Character 60: + + 0x00, // | | + 0x00, // | | + 0x20, // | # | + 0x40, // | # | + 0x80, // |# | + 0x40, // | # | + 0x20, // | # | + 0x00, // | | + + // Character 61: + + 0x00, // | | + 0x00, // | | + 0xe0, // |### | + 0x00, // | | + 0xe0, // |### | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 62: + + 0x00, // | | + 0x00, // | | + 0x80, // |# | + 0x40, // | # | + 0x20, // | # | + 0x40, // | # | + 0x80, // |# | + 0x00, // | | + + // Character 63: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0x20, // | # | + 0x40, // | # | + 0x00, // | | + 0x40, // | # | + 0x00, // | | + + // Character 64: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x80, // |# | + 0x80, // |# | + 0x60, // | ## | + 0x00, // | | + + // Character 65: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xe0, // |### | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + + // Character 66: + + 0x00, // | | + 0xc0, // |## | + 0xa0, // |# # | + 0xc0, // |## | + 0xa0, // |# # | + 0xa0, // |# # | + 0xc0, // |## | + 0x00, // | | + + // Character 67: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0x80, // |# | + 0x80, // |# | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 68: + + 0x00, // | | + 0xc0, // |## | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xc0, // |## | + 0x00, // | | + + // Character 69: + + 0x00, // | | + 0xe0, // |### | + 0x80, // |# | + 0xe0, // |### | + 0x80, // |# | + 0x80, // |# | + 0xe0, // |### | + 0x00, // | | + + // Character 70: + + 0x00, // | | + 0xe0, // |### | + 0x80, // |# | + 0xe0, // |### | + 0x80, // |# | + 0x80, // |# | + 0x80, // |# | + 0x00, // | | + + // Character 71: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0x80, // |# | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 72: + + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0xe0, // |### | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + + // Character 73: + + 0x00, // | | + 0xe0, // |### | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0xe0, // |### | + 0x00, // | | + + // Character 74: + + 0x00, // | | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 75: + + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0xc0, // |## | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + + // Character 76: + + 0x00, // | | + 0x80, // |# | + 0x80, // |# | + 0x80, // |# | + 0x80, // |# | + 0x80, // |# | + 0xe0, // |### | + 0x00, // | | + + // Character 77: + + 0x00, // | | + 0xa0, // |# # | + 0xe0, // |### | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + + // Character 78: + + 0x00, // | | + 0x20, // | # | + 0xa0, // |# # | + 0xe0, // |### | + 0xe0, // |### | + 0xa0, // |# # | + 0x80, // |# | + 0x00, // | | + + // Character 79: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 80: + + 0x00, // | | + 0xc0, // |## | + 0xa0, // |# # | + 0xa0, // |# # | + 0xc0, // |## | + 0x80, // |# | + 0x80, // |# | + 0x00, // | | + + // Character 81: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xc0, // |## | + 0x60, // | ## | + 0x00, // | | + + // Character 82: + + 0x00, // | | + 0xc0, // |## | + 0xa0, // |# # | + 0xa0, // |# # | + 0xc0, // |## | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + + // Character 83: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0x40, // | # | + 0x20, // | # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 84: + + 0x00, // | | + 0xe0, // |### | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x00, // | | + + // Character 85: + + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xe0, // |### | + 0x00, // | | + + // Character 86: + + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 87: + + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xe0, // |### | + 0xa0, // |# # | + 0x00, // | | + + // Character 88: + + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + + // Character 89: + + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x00, // | | + + // Character 90: + + 0x00, // | | + 0xe0, // |### | + 0x20, // | # | + 0x40, // | # | + 0x40, // | # | + 0x80, // |# | + 0xe0, // |### | + 0x00, // | | + + // Character 91: + + 0x00, // | | + 0x60, // | ## | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x60, // | ## | + 0x00, // | | + + // Character 92: + + 0x00, // | | + 0x80, // |# | + 0x80, // |# | + 0x40, // | # | + 0x40, // | # | + 0x20, // | # | + 0x20, // | # | + 0x00, // | | + + // Character 93: + + 0x00, // | | + 0xc0, // |## | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0xc0, // |## | + 0x00, // | | + + // Character 94: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 95: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0xf0, // |####| + + // Character 96: + + 0x00, // | | + 0x80, // |# | + 0x40, // | # | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 97: + + 0x00, // | | + 0x00, // | | + 0xc0, // |## | + 0x20, // | # | + 0x60, // | ## | + 0xa0, // |# # | + 0x60, // | ## | + 0x00, // | | + + // Character 98: + + 0x00, // | | + 0x80, // |# | + 0x80, // |# | + 0xc0, // |## | + 0xa0, // |# # | + 0xa0, // |# # | + 0xc0, // |## | + 0x00, // | | + + // Character 99: + + 0x00, // | | + 0x00, // | | + 0x60, // | ## | + 0x80, // |# | + 0x80, // |# | + 0x80, // |# | + 0x60, // | ## | + 0x00, // | | + + // Character 100: + + 0x00, // | | + 0x20, // | # | + 0x20, // | # | + 0x60, // | ## | + 0xa0, // |# # | + 0xa0, // |# # | + 0x60, // | ## | + 0x00, // | | + + // Character 101: + + 0x00, // | | + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0xe0, // |### | + 0x80, // |# | + 0x60, // | ## | + 0x00, // | | + + // Character 102: + + 0x00, // | | + 0x20, // | # | + 0x40, // | # | + 0xe0, // |### | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x00, // | | + + // Character 103: + + 0x00, // | | + 0x00, // | | + 0x60, // | ## | + 0xa0, // |# # | + 0xa0, // |# # | + 0x60, // | ## | + 0x20, // | # | + 0xc0, // |## | + + // Character 104: + + 0x00, // | | + 0x80, // |# | + 0x80, // |# | + 0xc0, // |## | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + + // Character 105: + + 0x00, // | | + 0x40, // | # | + 0x00, // | | + 0xc0, // |## | + 0x40, // | # | + 0x40, // | # | + 0xe0, // |### | + 0x00, // | | + + // Character 106: + + 0x00, // | | + 0x20, // | # | + 0x00, // | | + 0x60, // | ## | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0xc0, // |## | + + // Character 107: + + 0x00, // | | + 0x80, // |# | + 0x80, // |# | + 0xa0, // |# # | + 0xc0, // |## | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + + // Character 108: + + 0x00, // | | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x00, // | | + + // Character 109: + + 0x00, // | | + 0x00, // | | + 0xa0, // |# # | + 0xe0, // |### | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + + // Character 110: + + 0x00, // | | + 0x00, // | | + 0xc0, // |## | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + + // Character 111: + + 0x00, // | | + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 112: + + 0x00, // | | + 0x00, // | | + 0xc0, // |## | + 0xa0, // |# # | + 0xa0, // |# # | + 0xc0, // |## | + 0x80, // |# | + 0x80, // |# | + + // Character 113: + + 0x00, // | | + 0x00, // | | + 0x60, // | ## | + 0xa0, // |# # | + 0xa0, // |# # | + 0x60, // | ## | + 0x20, // | # | + 0x20, // | # | + + // Character 114: + + 0x00, // | | + 0x00, // | | + 0x60, // | ## | + 0x80, // |# | + 0x80, // |# | + 0x80, // |# | + 0x80, // |# | + 0x00, // | | + + // Character 115: + + 0x00, // | | + 0x00, // | | + 0x60, // | ## | + 0x80, // |# | + 0x40, // | # | + 0x20, // | # | + 0xc0, // |## | + 0x00, // | | + + // Character 116: + + 0x00, // | | + 0x40, // | # | + 0x40, // | # | + 0xe0, // |### | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x00, // | | + + // Character 117: + + 0x00, // | | + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xe0, // |### | + 0x00, // | | + + // Character 118: + + 0x00, // | | + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 119: + + 0x00, // | | + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xe0, // |### | + 0xa0, // |# # | + 0x00, // | | + + // Character 120: + + 0x00, // | | + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + + // Character 121: + + 0x00, // | | + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x60, // | ## | + 0x20, // | # | + 0xc0, // |## | + + // Character 122: + + 0x00, // | | + 0x00, // | | + 0xe0, // |### | + 0x20, // | # | + 0x40, // | # | + 0x80, // |# | + 0xe0, // |### | + 0x00, // | | + + // Character 123: + + 0x20, // | # | + 0x40, // | # | + 0x40, // | # | + 0x80, // |# | + 0x40, // | # | + 0x40, // | # | + 0x20, // | # | + 0x00, // | | + + // Character 124: + + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x00, // | | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x00, // | | + + // Character 125: + + 0x80, // |# | + 0x40, // | # | + 0x40, // | # | + 0x20, // | # | + 0x40, // | # | + 0x40, // | # | + 0x80, // |# | + 0x00, // | | + + // Character 126: + + 0x00, // | | + 0x50, // | # #| + 0xa0, // |# # | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 127: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xe0, // |### | + 0x00, // | | + 0x00, // | | + + // ------ Characters 128-255 are scaled-down from the full size ------ + // DOS font. Some of these have been fixed up, the rest + // need to be fixed up :-) + + // Character 128: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0x80, // |# | + 0x80, // |# | + 0xa0, // |# # | + 0x40, // | # | + 0xc0, // |## | + + + // Character 129: + + 0xa0, // |# # | + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xe0, // |### | + 0x00, // | | + + // Character 130: + + 0x40, // | # | + 0x80, // |# | + 0x40, // | # | + 0xa0, // |# # | + 0xe0, // |### | + 0x80, // |# | + 0x60, // | ## | + 0x00, // | | + + + // Character 131: + + 0x20, // | # | + 0x50, // | # #| + 0xc0, // |## | + 0x20, // | # | + 0x60, // | ## | + 0xa0, // |# # | + 0x60, // | ## | + 0x00, // | | + + // Character 132: + + 0xa0, // |# # | + 0x00, // | | + 0xc0, // |## | + 0x20, // | # | + 0x60, // | ## | + 0xa0, // |# # | + 0x60, // | ## | + 0x00, // | | + + // Character 133: + + 0x40, // | # | + 0x20, // | # | + 0xc0, // |## | + 0x20, // | # | + 0x60, // | ## | + 0xa0, // |# # | + 0x60, // | ## | + 0x00, // | | + + // Character 134: + + 0x40, // | # | + 0xa0, // |# # | + 0x40, // | # | + 0x20, // | # | + 0x60, // | ## | + 0xa0, // |# # | + 0x60, // | ## | + 0x00, // | | + + // Character 135: + + 0x00, // | | + 0x00, // | | + 0x60, // | ## | + 0x80, // |# | + 0x80, // |# | + 0x80, // |# | + 0x60, // | ## | + 0xc0, // |## | + + // Character 136: + + 0x40, // | # | + 0xa0, // |# # | + 0x40, // | # | + 0xa0, // |# # | + 0xe0, // |### | + 0x80, // |# | + 0x60, // | ## | + 0x00, // | | + + // Character 137: + + 0xa0, // |# # | + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0xe0, // |### | + 0x80, // |# | + 0x60, // | ## | + 0x00, // | | + + // Character 138: + + 0x40, // | # | + 0x20, // | # | + 0x40, // | # | + 0xa0, // |# # | + 0xe0, // |### | + 0x80, // |# | + 0x60, // | ## | + 0x00, // | | + + + // Character 139: + + 0x00, // | | + 0xa0, // |# # | + 0x00, // | | + 0xc0, // |## | + 0x40, // | # | + 0x40, // | # | + 0xe0, // |### | + 0x00, // | | + + // Character 140: + + 0x40, // | # | + 0xa0, // |# # | + 0x00, // | | + 0xc0, // |## | + 0x40, // | # | + 0x40, // | # | + 0xe0, // |### | + 0x00, // | | + + // Character 141: + + 0x40, // | # | + 0x20, // | # | + 0x00, // | | + 0xc0, // |## | + 0x40, // | # | + 0x40, // | # | + 0xe0, // |### | + 0x00, // | | + + // Character 142: + + 0xa0, // |# # | + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0xe0, // |### | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + + // Character 143: + + 0x40, // | # | + 0xa0, // |# # | + 0x40, // | # | + 0xa0, // |# # | + 0xe0, // |### | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + + // Character 144: + + 0x20, // | # | + 0x40, // | # | + 0xe0, // |### | + 0x80, // |# | + 0xc0, // |## | + 0x80, // |# | + 0xe0, // |### | + 0x00, // | | + + // Character 145: + + 0x00, // | | + 0x00, // | | + 0xb0, // |# ##| + 0x50, // | # #| + 0x70, // | ###| + 0xa0, // |# # | + 0x70, // | ###| + 0x00, // | | + + // Character 146: + + 0x00, // | | + 0x70, // | ###| + 0xa0, // |# # | + 0xf0, // |####| + 0xa0, // |# # | + 0xa0, // |# # | + 0xb0, // |# ##| + 0x00, // | | + + // Character 147: + + 0x40, // | # | + 0xa0, // |# # | + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 148: + + 0xa0, // |# # | + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 149: + + 0x40, // | # | + 0x20, // | # | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 150: + + 0x40, // | # | + 0xa0, // |# # | + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xe0, // |### | + 0x00, // | | + + // Character 151: + + 0x40, // | # | + 0x20, // | # | + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xe0, // |### | + 0x00, // | | + + // Character 152: + + 0xa0, // |# # | + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x60, // | ## | + 0x20, // | # | + 0xc0, // |## | + + // Character 153: + + 0xa0, // |# # | + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 154: + + 0xa0, // |# # | + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xe0, // |### | + 0x00, // | | + + + // Character 155: + + 0x00, // | | + 0x40, // | # | + 0x60, // | ## | + 0x80, // |# | + 0x80, // |# | + 0x60, // | ## | + 0x40, // | # | + 0x00, // | | + + // Character 156: + + 0x30, // | ##| + 0x40, // | # | + 0x40, // | # | + 0xe0, // |### | + 0x40, // | # | + 0x40, // | # | + 0xf0, // |####| + 0x00, // | | + + // Character 157: + + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0xe0, // |### | + 0x40, // | # | + 0x40, // | # | + 0x00, // | | + + // Character 158: + + 0x00, // | | + 0xc0, // |## | + 0xa0, // |# # | + 0xc0, // |## | + 0xa0, // |# # | + 0xb0, // |# ##| + 0xa0, // |# # | + 0x00, // | | + + // Character 159: + + 0x10, // | #| + 0x20, // | # | + 0x20, // | # | + 0x70, // | ###| + 0x20, // | # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 160: + + 0x20, // | # | + 0x40, // | # | + 0xc0, // |## | + 0x20, // | # | + 0x60, // | ## | + 0xa0, // |# # | + 0x60, // | ## | + 0x00, // | | + + // Character 161: + + 0x20, // | # | + 0x40, // | # | + 0x00, // | | + 0xc0, // |## | + 0x40, // | # | + 0x40, // | # | + 0xe0, // |### | + 0x00, // | | + + // Character 162: + + 0x40, // | # | + 0x80, // |# | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 163: + + 0x20, // | # | + 0x40, // | # | + 0x00, // | | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xe0, // |### | + 0x00, // | | + + + // Character 164: + + 0x50, // | # #| + 0xa0, // |# # | + 0x00, // | | + 0xc0, // |## | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + + // Character 165: + + 0x50, // | # #| + 0xa0, // |# # | + 0x00, // | | + 0x90, // |# #| + 0xd0, // |## #| + 0xb0, // |# ##| + 0x90, // |# #| + 0x00, // | | + + + // Character 166: + + 0x60, // | ## | + 0xa0, // |# # | + 0x70, // | ###| + 0x00, // | | + 0xf0, // |####| + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 167: + + 0x40, // | # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + 0xe0, // |### | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 168: + + 0x00, // | | + 0x40, // | # | + 0x00, // | | + 0x40, // | # | + 0x80, // |# | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 169: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0xe0, // |### | + 0x80, // |# | + 0x80, // |# | + 0x00, // | | + 0x00, // | | + + // Character 170: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x70, // | ###| + 0x10, // | #| + 0x10, // | #| + 0x00, // | | + 0x00, // | | + + // Character 171: + + 0x80, // |# | + 0x80, // |# | + 0xa0, // |# # | + 0x40, // | # | + 0xb0, // |# ##| + 0x10, // | #| + 0x20, // | # | + 0x30, // | ##| + + // Character 172: + + 0x80, // |# | + 0x80, // |# | + 0xa0, // |# # | + 0x40, // | # | + 0x80, // |# | + 0x50, // | # #| + 0x70, // | ###| + 0x10, // | #| + + // Character 173: + + 0x00, // | | + 0x40, // | # | + 0x00, // | | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x40, // | # | + 0x00, // | | + + // Character 174: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x50, // | # #| + 0xa0, // |# # | + 0x50, // | # #| + 0x00, // | | + 0x00, // | | + + // Character 175: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0xa0, // |# # | + 0x50, // | # #| + 0xa0, // |# # | + 0x00, // | | + 0x00, // | | + + // Character 176: + + 0x10, // | #| + 0x40, // | # | + 0x10, // | #| + 0x40, // | # | + 0x10, // | #| + 0x40, // | # | + 0x10, // | #| + 0x40, // | # | + + // Character 177: + + 0x50, // | # #| + 0xa0, // |# # | + 0x50, // | # #| + 0xa0, // |# # | + 0x50, // | # #| + 0xa0, // |# # | + 0x50, // | # #| + 0xa0, // |# # | + + // Character 178: + + 0xd0, // |## #| + 0x70, // | ###| + 0xd0, // |## #| + 0x70, // | ###| + 0xd0, // |## #| + 0x70, // | ###| + 0xd0, // |## #| + 0x70, // | ###| + + // Character 179: + + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + + // Character 180: + + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0xe0, // |### | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + + // Character 181: + + 0x20, // | # | + 0x20, // | # | + 0xe0, // |### | + 0x20, // | # | + 0xe0, // |### | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + + // Character 182: + + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0xd0, // |## #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + + // Character 183: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0xf0, // |####| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + + // Character 184: + + 0x00, // | | + 0x00, // | | + 0xe0, // |### | + 0x20, // | # | + 0xe0, // |### | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + + // Character 185: + + 0x50, // | # #| + 0x50, // | # #| + 0xd0, // |## #| + 0x10, // | #| + 0xd0, // |## #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + + // Character 186: + + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + + // Character 187: + + 0x00, // | | + 0x00, // | | + 0xf0, // |####| + 0x10, // | #| + 0xd0, // |## #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + + // Character 188: + + 0x50, // | # #| + 0x50, // | # #| + 0xd0, // |## #| + 0x10, // | #| + 0xf0, // |####| + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 189: + + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0xf0, // |####| + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 190: + + 0x20, // | # | + 0x20, // | # | + 0xe0, // |### | + 0x20, // | # | + 0xe0, // |### | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 191: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0xe0, // |### | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + + // Character 192: + + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x30, // | ##| + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 193: + + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0xf0, // |####| + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 194: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0xf0, // |####| + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + + // Character 195: + + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x30, // | ##| + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + + // Character 196: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0xf0, // |####| + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 197: + + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0xf0, // |####| + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + + // Character 198: + + 0x20, // | # | + 0x20, // | # | + 0x30, // | ##| + 0x20, // | # | + 0x30, // | ##| + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + + // Character 199: + + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + + // Character 200: + + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x40, // | # | + 0x70, // | ###| + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 201: + + 0x00, // | | + 0x00, // | | + 0x70, // | ###| + 0x40, // | # | + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + + // Character 202: + + 0x50, // | # #| + 0x50, // | # #| + 0xd0, // |## #| + 0x00, // | | + 0xf0, // |####| + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 203: + + 0x00, // | | + 0x00, // | | + 0xf0, // |####| + 0x00, // | | + 0xd0, // |## #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + + // Character 204: + + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + + // Character 205: + + 0x00, // | | + 0x00, // | | + 0xf0, // |####| + 0x00, // | | + 0xf0, // |####| + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 206: + + 0x50, // | # #| + 0x50, // | # #| + 0xd0, // |## #| + 0x00, // | | + 0xd0, // |## #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + + // Character 207: + + 0x20, // | # | + 0x20, // | # | + 0xf0, // |####| + 0x00, // | | + 0xf0, // |####| + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 208: + + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0xf0, // |####| + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 209: + + 0x00, // | | + 0x00, // | | + 0xf0, // |####| + 0x00, // | | + 0xf0, // |####| + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + + // Character 210: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0xf0, // |####| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + + // Character 211: + + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x70, // | ###| + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 212: + + 0x20, // | # | + 0x20, // | # | + 0x30, // | ##| + 0x20, // | # | + 0x30, // | ##| + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 213: + + 0x00, // | | + 0x00, // | | + 0x30, // | ##| + 0x20, // | # | + 0x30, // | ##| + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + + // Character 214: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x70, // | ###| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + + // Character 215: + + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0xf0, // |####| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + 0x50, // | # #| + + // Character 216: + + 0x20, // | # | + 0x20, // | # | + 0xf0, // |####| + 0x20, // | # | + 0xf0, // |####| + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + + // Character 217: + + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0xe0, // |### | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 218: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x30, // | ##| + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + + // Character 219: + + 0xf0, // |####| + 0xf0, // |####| + 0xf0, // |####| + 0xf0, // |####| + 0xf0, // |####| + 0xf0, // |####| + 0xf0, // |####| + 0xf0, // |####| + + // Character 220: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0xf0, // |####| + 0xf0, // |####| + 0xf0, // |####| + 0xf0, // |####| + + // Character 221: + + 0xc0, // |## | + 0xc0, // |## | + 0xc0, // |## | + 0xc0, // |## | + 0xc0, // |## | + 0xc0, // |## | + 0xc0, // |## | + 0xc0, // |## | + + // Character 222: + + 0x30, // | ##| + 0x30, // | ##| + 0x30, // | ##| + 0x30, // | ##| + 0x30, // | ##| + 0x30, // | ##| + 0x30, // | ##| + 0x30, // | ##| + + // Character 223: + + 0xf0, // |####| + 0xf0, // |####| + 0xf0, // |####| + 0xf0, // |####| + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 224: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x50, // | # #| + 0xa0, // |# # | + 0xa0, // |# # | + 0x50, // | # #| + 0x00, // | | + + // Character 225: + + 0x00, // | | + 0xc0, // |## | + 0xa0, // |# # | + 0xc0, // |## | + 0xa0, // |# # | + 0x90, // |# #| + 0xa0, // |# # | + 0x00, // | | + + // Character 226: + + 0x00, // | | + 0xf0, // |####| + 0x90, // |# #| + 0x80, // |# | + 0x80, // |# | + 0x80, // |# | + 0x80, // |# | + 0x00, // | | + + // Character 227: + + 0x00, // | | + 0x00, // | | + 0xf0, // |####| + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + + // Character 228: + + 0x00, // | | + 0x00, // | | + 0xe0, // |### | + 0x80, // |# | + 0x40, // | # | + 0x80, // |# | + 0xe0, // |### | + 0x00, // | | + + // Character 229: + + 0x00, // | | + 0x00, // | | + 0x70, // | ###| + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 230: + + 0x00, // | | + 0x00, // | | + 0x50, // | # #| + 0x50, // | # #| + 0x70, // | ###| + 0x40, // | # | + 0x80, // |# | + 0x00, // | | + + // Character 231: + + 0x00, // | | + 0x00, // | | + 0x50, // | # #| + 0xa0, // |# # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x00, // | | + + // Character 232: + + 0x00, // | | + 0xe0, // |### | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0xe0, // |### | + 0x00, // | | + + // Character 233: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0xe0, // |### | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 234: + + 0x00, // | | + 0x60, // | ## | + 0x90, // |# #| + 0x90, // |# #| + 0x60, // | ## | + 0x60, // | ## | + 0xf0, // |####| + 0x00, // | | + + // Character 235: + + + 0x00, // | | + 0x60, // | ## | + 0x80, // |# | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + + // Character 236: + + 0x00, // | | + 0x00, // | | + 0x60, // | ## | + 0xb0, // |# ##| + 0xd0, // |## #| + 0x60, // | ## | + 0x00, // | | + 0x00, // | | + + // Character 237: + + 0x00, // | | + 0x10, // | #| + 0xf0, // |####| + 0x90, // |# #| + 0x90, // |# #| + 0xf0, // |####| + 0x80, // |# | + 0x00, // | | + + // Character 238: + + 0x00, // | | + 0x60, // | ## | + 0x80, // |# | + 0xe0, // |### | + 0x80, // |# | + 0x80, // |# | + 0x60, // | ## | + 0x00, // | | + + // Character 239: + + 0x00, // | | + 0x40, // | # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + + // Character 240: + + 0x00, // | | + 0xe0, // |### | + 0x00, // | | + 0xe0, // |### | + 0x00, // | | + 0xe0, // |### | + 0x00, // | | + 0x00, // | | + + // Character 241: + + 0x00, // | | + 0x40, // | # | + 0xe0, // |### | + 0x40, // | # | + 0x00, // | | + 0xe0, // |### | + 0x00, // | | + 0x00, // | | + + // Character 242: + + 0x00, // | | + 0x80, // |# | + 0x40, // | # | + 0x20, // | # | + 0x40, // | # | + 0x80, // |# | + 0xe0, // |### | + 0x00, // | | + + // Character 243: + + 0x00, // | | + 0x20, // | # | + 0x40, // | # | + 0x80, // |# | + 0x40, // | # | + 0x20, // | # | + 0xe0, // |### | + 0x00, // | | + + // Character 244: + + 0x00, // | | + 0x10, // | #| + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + + // Character 245: + + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0x20, // | # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + 0x00, // | | + + // Character 246: + + 0x00, // | | + 0x00, // | | + 0x40, // | # | + 0x00, // | | + 0xe0, // |### | + 0x00, // | | + 0x40, // | # | + 0x00, // | | + + // Character 247: + + 0x00, // | | + 0x50, // | # #| + 0xa0, // |# # | + 0x00, // | | + 0x50, // | # #| + 0xa0, // |# # | + 0x00, // | | + 0x00, // | | + + // Character 248: + + 0x40, // | # | + 0xa0, // |# # | + 0x40, // | # | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 249: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x60, // | ## | + 0x60, // | ## | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 250: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x40, // | # | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 251: + + 0x30, // | ##| + 0x20, // | # | + 0x20, // | # | + 0xa0, // |# # | + 0x60, // | ## | + 0x20, // | # | + 0x00, // | | + 0x00, // | | + + // Character 252: + + 0xe0, // |### | + 0xa0, // |# # | + 0xa0, // |# # | + 0xa0, // |# # | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 253: + + 0xc0, // |## | + 0x20, // | # | + 0x40, // | # | + 0xe0, // |### | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + + // Character 254: + + 0x00, // | | + 0x00, // | | + 0xf0, // |####| + 0xf0, // |####| + 0xf0, // |####| + 0xf0, // |####| + 0x00, // | | + 0x00, // | | + + // Character 255: + + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | + 0x00, // | | +}; + +static txt_font_t small_font = +{ + small_font_data, + 4, // width + 8 // height +}; + + diff --git a/src/WIN/win_opendir.c b/src/WIN/win_opendir.c new file mode 100644 index 0000000..b2f6780 --- /dev/null +++ b/src/WIN/win_opendir.c @@ -0,0 +1,336 @@ +// +// 03/10/2006 James Haley +// +// For this module only: +// This code is public domain. No change sufficient enough to constitute a +// significant or original work has been made, and thus it remains as such. +// +// +// DESCRIPTION: +// +// Implementation of POSIX opendir for Visual C++. +// Derived from the MinGW C Library Extensions Source (released to the +// public domain). As with other Win32 modules, don't include most DOOM +// headers into this or conflicts will occur. +// +// Original Header: +// +// * dirent.c +// * This file has no copyright assigned and is placed in the Public Domain. +// * This file is a part of the mingw-runtime package. +// * No warranty is given; refer to the file DISCLAIMER within the package. +// * +// * Derived from DIRLIB.C by Matt J. Weinstein +// * This note appears in the DIRLIB.H +// * DIRLIB.H by M. J. Weinstein Released to public domain 1-Jan-89 +// * +// * Updated by Jeremy Bettis +// * Significantly revised and rewinddir, seekdir and telldir added by Colin +// * Peters +// + +#ifndef _MSC_VER +#error i_opndir.c is for Microsoft Visual C++ only +#endif + +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include /* for GetFileAttributes */ + +#include +#define SUFFIX _T("*") +#define SLASH _T("\\") + +#include "win_opendir.h" + +// +// opendir +// +// Returns a pointer to a DIR structure appropriately filled in to begin +// searching a directory. +// +DIR *opendir(const _TCHAR *szPath) +{ + DIR *nd; + unsigned int rc; + _TCHAR szFullPath[MAX_PATH]; + + errno = 0; + + if(!szPath) + { + errno = EFAULT; + return (DIR *)0; + } + + if(szPath[0] == _T('\0')) + { + errno = ENOTDIR; + return (DIR *)0; + } + + /* Attempt to determine if the given path really is a directory. */ + rc = GetFileAttributes(szPath); + if(rc == (unsigned int)-1) + { + /* call GetLastError for more error info */ + errno = ENOENT; + return (DIR *)0; + } + if(!(rc & FILE_ATTRIBUTE_DIRECTORY)) + { + /* Error, entry exists but not a directory. */ + errno = ENOTDIR; + return (DIR *)0; + } + + /* Make an absolute pathname. */ + _tfullpath(szFullPath, szPath, MAX_PATH); + + /* Allocate enough space to store DIR structure and the complete + * directory path given. */ + nd = (DIR *)(malloc(sizeof(DIR) + (_tcslen(szFullPath) + + _tcslen(SLASH) + + _tcslen(SUFFIX) + 1) + * sizeof(_TCHAR))); + + if(!nd) + { + /* Error, out of memory. */ + errno = ENOMEM; + return (DIR *)0; + } + + /* Create the search expression. */ + _tcscpy(nd->dd_name, szFullPath); + + /* Add on a slash if the path does not end with one. */ + if(nd->dd_name[0] != _T('\0') + && _tcsrchr(nd->dd_name, _T('/')) != nd->dd_name + + _tcslen(nd->dd_name) - 1 + && _tcsrchr(nd->dd_name, _T('\\')) != nd->dd_name + + _tcslen(nd->dd_name) - 1) + { + _tcscat(nd->dd_name, SLASH); + } + + /* Add on the search pattern */ + _tcscat(nd->dd_name, SUFFIX); + + /* Initialize handle to -1 so that a premature closedir doesn't try + * to call _findclose on it. */ + nd->dd_handle = -1; + + /* Initialize the status. */ + nd->dd_stat = 0; + + /* Initialize the dirent structure. ino and reclen are invalid under + * Win32, and name simply points at the appropriate part of the + * findfirst_t structure. */ + nd->dd_dir.d_ino = 0; + nd->dd_dir.d_reclen = 0; + nd->dd_dir.d_namlen = 0; + memset(nd->dd_dir.d_name, 0, FILENAME_MAX); + + return nd; +} + +// +// readdir +// +// Return a pointer to a dirent structure filled with the information on the +// next entry in the directory. +// +struct dirent *readdir(DIR *dirp) +{ + errno = 0; + + /* Check for valid DIR struct. */ + if(!dirp) + { + errno = EFAULT; + return (struct dirent *)0; + } + + if (dirp->dd_stat < 0) + { + /* We have already returned all files in the directory + * (or the structure has an invalid dd_stat). */ + return (struct dirent *)0; + } + else if (dirp->dd_stat == 0) + { + /* We haven't started the search yet. */ + /* Start the search */ + dirp->dd_handle = _tfindfirst(dirp->dd_name, &(dirp->dd_dta)); + + if(dirp->dd_handle == -1) + { + /* Whoops! Seems there are no files in that + * directory. */ + dirp->dd_stat = -1; + } + else + { + dirp->dd_stat = 1; + } + } + else + { + /* Get the next search entry. */ + if(_tfindnext(dirp->dd_handle, &(dirp->dd_dta))) + { + /* We are off the end or otherwise error. + _findnext sets errno to ENOENT if no more file + Undo this. */ + DWORD winerr = GetLastError(); + if(winerr == ERROR_NO_MORE_FILES) + errno = 0; + _findclose(dirp->dd_handle); + dirp->dd_handle = -1; + dirp->dd_stat = -1; + } + else + { + /* Update the status to indicate the correct + * number. */ + dirp->dd_stat++; + } + } + + if (dirp->dd_stat > 0) + { + /* Successfully got an entry. Everything about the file is + * already appropriately filled in except the length of the + * file name. */ + dirp->dd_dir.d_namlen = _tcslen(dirp->dd_dta.name); + _tcscpy(dirp->dd_dir.d_name, dirp->dd_dta.name); + return &dirp->dd_dir; + } + + return (struct dirent *)0; +} + + +// +// closedir +// +// Frees up resources allocated by opendir. +// +int closedir(DIR *dirp) +{ + int rc; + + errno = 0; + rc = 0; + + if(!dirp) + { + errno = EFAULT; + return -1; + } + + if(dirp->dd_handle != -1) + { + rc = _findclose(dirp->dd_handle); + } + + /* Delete the dir structure. */ + free(dirp); + + return rc; +} + +// +// rewinddir +// +// Return to the beginning of the directory "stream". We simply call findclose +// and then reset things like an opendir. +// +void rewinddir(DIR * dirp) +{ + errno = 0; + + if(!dirp) + { + errno = EFAULT; + return; + } + + if(dirp->dd_handle != -1) + { + _findclose(dirp->dd_handle); + } + + dirp->dd_handle = -1; + dirp->dd_stat = 0; +} + +// +// telldir +// +// Returns the "position" in the "directory stream" which can be used with +// seekdir to go back to an old entry. We simply return the value in stat. +// +long telldir(DIR *dirp) +{ + errno = 0; + + if(!dirp) + { + errno = EFAULT; + return -1; + } + return dirp->dd_stat; +} + +// +// seekdir +// +// Seek to an entry previously returned by telldir. We rewind the directory +// and call readdir repeatedly until either dd_stat is the position number +// or -1 (off the end). This is not perfect, in that the directory may +// have changed while we weren't looking. But that is probably the case with +// any such system. +// +void seekdir(DIR *dirp, long lPos) +{ + errno = 0; + + if(!dirp) + { + errno = EFAULT; + return; + } + + if(lPos < -1) + { + /* Seeking to an invalid position. */ + errno = EINVAL; + return; + } + else if(lPos == -1) + { + /* Seek past end. */ + if(dirp->dd_handle != -1) + { + _findclose(dirp->dd_handle); + } + dirp->dd_handle = -1; + dirp->dd_stat = -1; + } + else + { + /* Rewind and read forward to the appropriate index. */ + rewinddir(dirp); + + while((dirp->dd_stat < lPos) && readdir(dirp)) + ; /* do-nothing loop */ + } +} + +// EOF + diff --git a/src/WIN/win_opendir.h b/src/WIN/win_opendir.h new file mode 100644 index 0000000..c1844e1 --- /dev/null +++ b/src/WIN/win_opendir.h @@ -0,0 +1,73 @@ +// +// 03/10/2006 James Haley +// +// For this module only: +// This code is public domain. No change sufficient enough to constitute a +// significant or original work has been made, and thus it remains as such. +// +// +// DESCRIPTION: +// +// Implementation of POSIX opendir for Visual C++. +// Derived from the MinGW C Library Extensions Source (released to the +// public domain). +// + +#ifndef I_OPNDIR_H__ +#define I_OPNDIR_H__ + +#include + +#ifndef FILENAME_MAX +#define FILENAME_MAX 260 +#endif + +struct dirent +{ + long d_ino; /* Always zero. */ + unsigned short d_reclen; /* Always zero. */ + unsigned short d_namlen; /* Length of name in d_name. */ + char d_name[FILENAME_MAX]; /* File name. */ +}; + +/* + * This is an internal data structure. Good programmers will not use it + * except as an argument to one of the functions below. + * dd_stat field is now int (was short in older versions). + */ +typedef struct +{ + /* disk transfer area for this dir */ + struct _finddata_t dd_dta; + + /* dirent struct to return from dir (NOTE: this makes this thread + * safe as long as only one thread uses a particular DIR struct at + * a time) */ + struct dirent dd_dir; + + /* _findnext handle */ + intptr_t dd_handle; + + /* + * Status of search: + * 0 = not started yet (next entry to read is first entry) + * -1 = off the end + * positive = 0 based index of next entry + */ + int dd_stat; + + /* given path for dir with search pattern (struct is extended) */ + char dd_name[1]; +} DIR; + +DIR *opendir(const char *); +struct dirent *readdir(DIR *); +int closedir(DIR *); +void rewinddir(DIR *); +long telldir(DIR *); +void seekdir(DIR *, long); + +#endif + +// EOF + diff --git a/src/am_map.c b/src/am_map.c new file mode 100644 index 0000000..9ef6332 --- /dev/null +++ b/src/am_map.c @@ -0,0 +1,2373 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * the automap code + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#ifdef GL_DOOM +#include "gl_opengl.h" +#endif + +#include "doomstat.h" +#include "st_stuff.h" +#include "r_main.h" +#include "p_setup.h" +#include "p_maputl.h" +#include "w_wad.h" +#include "v_video.h" +#include "p_spec.h" +#include "am_map.h" +#include "d_deh.h" // Ty 03/27/98 - externalizations +#include "lprintf.h" // jff 08/03/98 - declaration of lprintf +#include "g_game.h" +#include "r_fps.h" +#include "r_demo.h" +#include "m_misc.h" +#include "m_bbox.h" + +extern dboolean gamekeydown[]; + +//jff 1/7/98 default automap colors added +int mapcolor_back; // map background +int mapcolor_grid; // grid lines color +int mapcolor_wall; // normal 1s wall color +int mapcolor_fchg; // line at floor height change color +int mapcolor_cchg; // line at ceiling height change color +int mapcolor_clsd; // line at sector with floor=ceiling color +int mapcolor_rkey; // red key color +int mapcolor_bkey; // blue key color +int mapcolor_ykey; // yellow key color +int mapcolor_rdor; // red door color (diff from keys to allow option) +int mapcolor_bdor; // blue door color (of enabling one but not other ) +int mapcolor_ydor; // yellow door color +int mapcolor_tele; // teleporter line color +int mapcolor_secr; // secret sector boundary color +int mapcolor_exit; // jff 4/23/98 add exit line color +int mapcolor_unsn; // computer map unseen line color +int mapcolor_flat; // line with no floor/ceiling changes +int mapcolor_sprt; // general sprite color +int mapcolor_item; // item sprite color +int mapcolor_frnd; // friendly sprite color +int mapcolor_enemy; // enemy sprite color +int mapcolor_hair; // crosshair color +int mapcolor_sngl; // single player arrow color +int mapcolor_plyr[4] = { 112, 96, 64, 176 }; // colors for player arrows in multiplayer + +//jff 3/9/98 add option to not show secret sectors until entered +int map_secret_after; + +int map_always_updates; +int map_grid_size; +int map_scroll_speed; +int map_wheel_zoom; +int map_use_multisamling; +int map_textured; +int map_textured_trans; +int map_textured_overlay_trans; +int map_lines_overlay_trans; +int map_overlay_pos_x; +int map_overlay_pos_y; +int map_overlay_pos_width; +int map_overlay_pos_height; + +map_things_appearance_t map_things_appearance; +const char *map_things_appearance_list[map_things_appearance_max] = +{ + "classic", + "scaled", +#if defined(HAVE_LIBSDL2_IMAGE) && defined(GL_DOOM) + "icons" +#endif +}; + +// drawing stuff +#define FB 0 + +// 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 (gamekeydown[key_speed] ? map_scroll_speed * 2 : map_scroll_speed) +// how much zoom-in per tic +// goes to 2x in 1 second +#define M_ZOOMIN ((int) ((float)FRACUNIT * (1.00f + F_PANINC / 200.0f))) +// how much zoom-out per tic +// pulls out to 0.5x in 1 second +#define M_ZOOMOUT ((int) ((float)FRACUNIT / (1.00f + F_PANINC / 200.0f))) + +#define PLAYERRADIUS (16*(1<>16) +#define MTOF(x) (fixed_t)((((int_64_t)(x) * scale_mtof) >> FRACBITS)>>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))) +// precise versions +#define MTOF_F(x) (((float)(x)*scale_mtof)/(float)FRACUNIT/(float)FRACUNIT) +#define CXMTOF_F(x) ((float)f_x + MTOF_F((x)-m_x)) +#define CYMTOF_F(y) ((float)f_y + (f_h - MTOF_F((y)-m_y))) + +typedef struct +{ + mpoint_t a, b; +} mline_t; + +// +// The vector graphics for the automap. +// A line drawing of the player pointing right, +// starting from the middle. +// +#define R ((8*PLAYERRADIUS)/7) +mline_t player_arrow[] = +{ + { { -R+R/8, 0 }, { R, 0 } }, // ----- + { { R, 0 }, { R-R/2, R/4 } }, // -----> + { { R, 0 }, { R-R/2, -R/4 } }, + { { -R+R/8, 0 }, { -R-R/8, R/4 } }, // >----> + { { -R+R/8, 0 }, { -R-R/8, -R/4 } }, + { { -R+3*R/8, 0 }, { -R+R/8, R/4 } }, // >>---> + { { -R+3*R/8, 0 }, { -R+R/8, -R/4 } } +}; +#undef R +#define NUMPLYRLINES (sizeof(player_arrow)/sizeof(mline_t)) + +#define R ((8*PLAYERRADIUS)/7) +mline_t cheat_player_arrow[] = +{ // killough 3/22/98: He's alive, Jim :) + { { -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 } }, + { { -R/10-R/6, R/4}, {-R/10-R/6, -R/4} }, // J + { { -R/10-R/6, -R/4}, {-R/10-R/6-R/8, -R/4} }, + { { -R/10-R/6-R/8, -R/4}, {-R/10-R/6-R/8, -R/8} }, + { { -R/10, R/4}, {-R/10, -R/4}}, // F + { { -R/10, R/4}, {-R/10+R/8, R/4}}, + { { -R/10+R/4, R/4}, {-R/10+R/4, -R/4}}, // F + { { -R/10+R/4, R/4}, {-R/10+R/4+R/8, R/4}}, +}; +#undef R +#define NUMCHEATPLYRLINES (sizeof(cheat_player_arrow)/sizeof(mline_t)) + +//jff 1/5/98 new symbol for keys on automap +#define R (FRACUNIT) +mline_t cross_mark[] = +{ + { { -R, 0 }, { R, 0} }, + { { 0, -R }, { 0, R } }, +}; +#undef R +#define NUMCROSSMARKLINES (sizeof(cross_mark)/sizeof(mline_t)) +//jff 1/5/98 end of new symbol + +#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 +#define NUMTHINTRIANGLEGUYLINES (sizeof(thintriangle_guy)/sizeof(mline_t)) + +int ddt_cheating = 0; // killough 2/7/98: make global, rename to ddt_* + +static int leveljuststarted = 1; // kluge until AM_LevelInit() is called + +enum automapmode_e automapmode; // Mode that the automap is in + +// 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 mpoint_t m_paninc; // how far the window pans each tic (map coords) +static fixed_t mtof_zoommul; // how far the window zooms each tic (map coords) +static fixed_t ftom_zoommul; // how far the window zooms each tic (fb coords) +static fixed_t curr_mtof_zoommul; + +static fixed_t m_x, m_y; // LL x,y window location on the map (map coords) +static fixed_t m_x2, m_y2; // UR x,y window location on the map (map coords) + +static fixed_t prev_m_x, prev_m_y; + +// +// 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 + +static fixed_t min_scale_mtof; // used to tell when to stop zooming out +static fixed_t max_scale_mtof; // used to tell when to stop zooming in + +// old stuff for recovery later +static fixed_t old_m_w, old_m_h; +static fixed_t old_m_x, old_m_y; + +// 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 fixed_t prev_scale_mtof = (fixed_t)INITSCALEMTOF; + +static player_t *plr; // the player represented by an arrow + +// killough 2/22/98: Remove limit on automap marks, +// and make variables external for use in savegames. + +markpoint_t *markpoints = NULL; // where the points are +int markpointnum = 0; // next point to be assigned (also number of points now) +int markpointnum_max = 0; // killough 2/22/98 + +static dboolean stopped = true; + +am_frame_t am_frame; + +array_t map_lines; + +static void AM_rotate(fixed_t* x, fixed_t* y, angle_t a); + +static void AM_SetMPointFloatValue(mpoint_t *p) +{ + if (am_frame.precise) + { + p->fx = (float)p->x; + p->fy = (float)p->y; + } +} + +static void AM_SetFPointFloatValue(fpoint_t *p) +{ +#ifdef GL_DOOM + p->fx = (float)p->x; + p->fy = (float)p->y; +#endif +} + +// +// AM_activateNewScale() +// +// Changes the map scale after zooming or translating +// +// Passed nothing, returns nothing +// +static void AM_activateNewScale(void) +{ + m_x += m_w/2; + m_y += m_h/2; + m_w = FTOM(f_w); + m_h = FTOM(f_h); + m_x -= m_w/2; + m_y -= m_h/2; + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; +} + +// +// AM_saveScaleAndLoc() +// +// Saves the current center and zoom +// Affects the variables that remember old scale and loc +// +// Passed nothing, returns nothing +// +static void AM_saveScaleAndLoc(void) +{ + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; +} + +// +// AM_restoreScaleAndLoc() +// +// restores the center and zoom from locally saved values +// Affects global variables for location and scale +// +// Passed nothing, returns nothing +// +static void AM_restoreScaleAndLoc(void) +{ + m_w = old_m_w; + m_h = old_m_h; + if (!(automapmode & am_follow)) + { + m_x = old_m_x; + m_y = old_m_y; + } + else + { + m_x = (viewx >> FRACTOMAPBITS) - m_w/2;//e6y + m_y = (viewy >> FRACTOMAPBITS) - m_h/2;//e6y + } + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; + + // Change the scaling multipliers + scale_mtof = FixedDiv(f_w<= markpointnum_max) + markpoints = realloc(markpoints, + (markpointnum_max = markpointnum_max ? + markpointnum_max*2 : 16) * sizeof(*markpoints)); + + markpoints[markpointnum].x = m_x + m_w/2; + markpoints[markpointnum].y = m_y + m_h/2; + AM_setMarkParams(markpointnum); + markpointnum++; +} + +// +// AM_findMinMaxBoundaries() +// +// Determines bounding box of all vertices, +// sets global variables controlling zoom range. +// +// Passed nothing, returns nothing +// +static void AM_findMinMaxBoundaries(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 max_x) + max_x = vertexes[i].x; + + if (vertexes[i].y < min_y) + min_y = vertexes[i].y; + else if (vertexes[i].y > max_y) + max_y = vertexes[i].y; + } + + max_w = (max_x >>= FRACTOMAPBITS) - (min_x >>= FRACTOMAPBITS);//e6y + max_h = (max_y >>= FRACTOMAPBITS) - (min_y >>= FRACTOMAPBITS);//e6y + + a = FixedDiv(f_w< 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; +} + +// +// AM_SetScale +// +void AM_SetScale(void) +{ + AM_findMinMaxBoundaries(); + scale_mtof = FixedDiv(min_scale_mtof, (int) (0.7*FRACUNIT)); + if (scale_mtof > max_scale_mtof) + scale_mtof = min_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); +} + +// +// AM_SetPosition +// +void AM_SetPosition(void) +{ + if (automapmode & am_overlay) + { + f_x = map_overlay_pos_x * SCREENWIDTH / 320; + f_y = map_overlay_pos_y * SCREENHEIGHT / 200; + f_w = map_overlay_pos_width * SCREENWIDTH / 320; + f_h = map_overlay_pos_height * SCREENHEIGHT / 200; + + if (f_x + f_w > SCREENWIDTH) + f_w = SCREENWIDTH - f_x; + if (f_y + f_h > SCREENHEIGHT) + f_h = SCREENHEIGHT - f_y; + + f_x = viewwindowx + f_x * viewwidth / SCREENWIDTH; + f_y = viewwindowy + f_y * viewheight / SCREENHEIGHT; + f_w = f_w * viewwidth / SCREENWIDTH; + f_h = f_h * viewheight / SCREENHEIGHT; + } + else + { + //default + f_x = f_y = 0; + f_w = SCREENWIDTH; // killough 2/7/98: get rid of finit_ vars + f_h = SCREENHEIGHT-ST_SCALED_HEIGHT;// to allow runtime setting of width/height + } +} + +// +// AM_initVariables() +// +// Initialize the variables for the automap +// +// Affects the automap global variables +// Status bar is notified that the automap has been entered +// Passed nothing, returns nothing +// +static void AM_initVariables(void) +{ + int pnum; + static event_t st_notify = { ev_keyup, AM_MSGENTERED, 0, 0 }; + + automapmode |= am_active; + + m_paninc.x = m_paninc.y = 0; + ftom_zoommul = FRACUNIT; + mtof_zoommul = FRACUNIT; + + m_w = FTOM(f_w); + m_h = FTOM(f_h); + + // find player to center on initially + if (!playeringame[pnum = consoleplayer]) + for (pnum=0;pnummo->x >> FRACTOMAPBITS) - m_w/2;//e6y + m_y = (plr->mo->y >> FRACTOMAPBITS) - m_h/2;//e6y + AM_Ticker(); + AM_changeWindowLoc(); + + // for saving & restoring + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; + + // inform the status bar of the change + ST_Responder(&st_notify); +} + +void AM_SetResolution(void) +{ + AM_SetPosition(); + AM_SetScale(); +} + +// +// AM_loadPics() +// +static void AM_loadPics(void) +{ + // cph - mark numbers no longer needed cached +} + +// +// AM_unloadPics() +// +static void AM_unloadPics(void) +{ + // cph - mark numbers no longer needed cached +} + +// +// AM_clearMarks() +// +// Sets the number of marks to 0, thereby clearing them from the display +// +// Affects the global variable markpointnum +// Passed nothing, returns nothing +// +void AM_clearMarks(void) +{ + markpointnum = 0; +} + +// +// AM_LevelInit() +// +// Initialize the automap at the start of a new level +// should be called at the start of every level +// +// Passed nothing, returns nothing +// Affects automap's global variables +// +// CPhipps - get status bar height from status bar code +static void AM_LevelInit(void) +{ + leveljuststarted = 0; + + AM_SetPosition(); + AM_SetScale(); +} + +// +// AM_Stop() +// +// Cease automap operations, unload patches, notify status bar +// +// Passed nothing, returns nothing +// +void AM_Stop (void) +{ + static event_t st_notify = { 0, ev_keyup, AM_MSGEXITED, 0 }; + + AM_unloadPics(); + automapmode &= ~am_active; + ST_Responder(&st_notify); + stopped = true; +} + +// +// AM_Start() +// +// Start up automap operations, +// if a new level, or game start, (re)initialize level variables +// init map variables +// load mark patches +// +// Passed nothing, returns nothing +// +void AM_Start(void) +{ + static int lastlevel = -1, lastepisode = -1; + + if (!stopped) + AM_Stop(); + stopped = false; + if (lastlevel != gamemap || lastepisode != gameepisode) + { + AM_LevelInit(); + lastlevel = gamemap; + lastepisode = gameepisode; + } + AM_SetPosition(); + AM_initVariables(); + AM_loadPics(); +} + +// +// AM_minOutWindowScale() +// +// Set the window scale to the maximum size +// +// Passed nothing, returns nothing +// +static void AM_minOutWindowScale(void) +{ + scale_mtof = min_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + AM_activateNewScale(); +} + +// +// AM_maxOutWindowScale(void) +// +// Set the window scale to the minimum size +// +// Passed nothing, returns nothing +// +static void AM_maxOutWindowScale(void) +{ + scale_mtof = max_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + AM_activateNewScale(); +} + +// +// AM_Responder() +// +// Handle events (user inputs) in automap mode +// +// Passed an input event, returns true if its handled +// +dboolean AM_Responder +( event_t* ev ) +{ + int rc; + static int bigstate=0; + int ch; // phares + + rc = false; + + if (!(automapmode & am_active)) + { + if (ev->type == ev_keydown && ev->data1 == key_map) // phares + { + AM_Start (); + rc = true; + } + } + else if (ev->type == ev_keydown) + { + rc = true; + ch = ev->data1; // phares + if (ch == key_map_right) // | + if (!(automapmode & am_follow)) // V + m_paninc.x = FTOM(F_PANINC); + else + rc = false; + else if (ch == key_map_left) + if (!(automapmode & am_follow)) + m_paninc.x = -FTOM(F_PANINC); + else + rc = false; + else if (ch == key_map_up) + if (!(automapmode & am_follow)) + m_paninc.y = FTOM(F_PANINC); + else + rc = false; + else if (ch == key_map_down) + if (!(automapmode & am_follow)) + m_paninc.y = -FTOM(F_PANINC); + else + rc = false; + else if ((ch == key_map_zoomout) || (map_wheel_zoom && ch == KEYD_MWHEELDOWN)) + { + mtof_zoommul = M_ZOOMOUT; + ftom_zoommul = M_ZOOMIN; + curr_mtof_zoommul = mtof_zoommul; + } + else if ((ch == key_map_zoomin) || (map_wheel_zoom && ch == KEYD_MWHEELUP)) + { + mtof_zoommul = M_ZOOMIN; + ftom_zoommul = M_ZOOMOUT; + curr_mtof_zoommul = mtof_zoommul; + } + else if (ch == key_map) + { + bigstate = 0; + AM_Stop (); + } + else if (ch == key_map_gobig) + { + bigstate = !bigstate; + if (bigstate) + { + AM_saveScaleAndLoc(); + AM_minOutWindowScale(); + } + else + AM_restoreScaleAndLoc(); + } + else if (ch == key_map_follow) + { + automapmode ^= am_follow; // CPhipps - put all automap mode stuff into one enum + // Ty 03/27/98 - externalized + plr->message = (automapmode & am_follow) ? s_AMSTR_FOLLOWON : s_AMSTR_FOLLOWOFF; + } + else if (ch == key_map_grid) + { + automapmode ^= am_grid; // CPhipps + // Ty 03/27/98 - *not* externalized + plr->message = (automapmode & am_grid) ? s_AMSTR_GRIDON : s_AMSTR_GRIDOFF; + } + else if (ch == key_map_mark) + { + /* Ty 03/27/98 - *not* externalized + * cph 2001/11/20 - use doom_printf so we don't have our own buffer */ + doom_printf("%s %d", s_AMSTR_MARKEDSPOT, markpointnum); + AM_addMark(); + } + else if (ch == key_map_clear) + { + AM_clearMarks(); // Ty 03/27/98 - *not* externalized + plr->message = s_AMSTR_MARKSCLEARED; // ^ + } // | + else if (ch == key_map_rotate) { + automapmode ^= am_rotate; + plr->message = (automapmode & am_rotate) ? s_AMSTR_ROTATEON : s_AMSTR_ROTATEOFF; + } + else if (ch == key_map_overlay) { + automapmode ^= am_overlay; + AM_SetPosition(); + AM_activateNewScale(); + plr->message = (automapmode & am_overlay) ? s_AMSTR_OVERLAYON : s_AMSTR_OVERLAYOFF; + } +#ifdef GL_DOOM + else if (ch == key_map_textured) { + map_textured = !map_textured; + M_ChangeMapTextured(); + plr->message = (map_textured ? s_AMSTR_TEXTUREDON : s_AMSTR_TEXTUREDOFF); + } +#endif + else // phares + { + rc = false; + } + } + else if (ev->type == ev_keyup) + { + rc = false; + ch = ev->data1; + if (ch == key_map_right) + { + if (!(automapmode & am_follow)) + m_paninc.x = 0; + } + else if (ch == key_map_left) + { + if (!(automapmode & am_follow)) + m_paninc.x = 0; + } + else if (ch == key_map_up) + { + if (!(automapmode & am_follow)) + m_paninc.y = 0; + } + else if (ch == key_map_down) + { + if (!(automapmode & am_follow)) + m_paninc.y = 0; + } + else if ((ch == key_map_zoomout) || (ch == key_map_zoomin) || + (map_wheel_zoom && ((ch == KEYD_MWHEELDOWN) || (ch == KEYD_MWHEELUP)))) + { + mtof_zoommul = FRACUNIT; + ftom_zoommul = FRACUNIT; + } + } + return rc; +} + +// +// AM_rotate() +// +// Rotation in 2D. +// Used to rotate player arrow line character. +// +// Passed the coordinates of a point, and an angle +// Returns the coordinates rotated by the angle +// +// CPhipps - made static & enhanced for automap rotation + +static void AM_rotate(fixed_t* x, fixed_t* y, angle_t a) +{ + fixed_t tmpx; + + tmpx = FixedMul(*x, finecosine[a>>ANGLETOFINESHIFT]) - + FixedMul(*y, finesine[a>>ANGLETOFINESHIFT]); + + *y = FixedMul(*x, finesine[a>>ANGLETOFINESHIFT]) + + FixedMul(*y, finecosine[a>>ANGLETOFINESHIFT]); + + *x = tmpx; +} + +void AM_rotatePoint(mpoint_t *p) +{ + fixed_t tmpx; + + if (am_frame.precise) + { + float f; + + p->fx = (float)p->x - am_frame.centerx_f; + p->fy = (float)p->y - am_frame.centery_f; + + f = (p->fx * am_frame.cos_f) - (p->fy * am_frame.sin_f) + am_frame.centerx_f; + p->fy = (p->fx * am_frame.sin_f) + (p->fy * am_frame.cos_f) + am_frame.centery_f; + p->fx = f; + } + + p->x -= am_frame.centerx; + p->y -= am_frame.centery; + + tmpx = FixedMul(p->x, am_frame.cos) - FixedMul(p->y, am_frame.sin) + am_frame.centerx; + p->y = FixedMul(p->x, am_frame.sin) + FixedMul(p->y, am_frame.cos) + am_frame.centery; + p->x = tmpx; +} + +// +// AM_changeWindowScale() +// +// Automap zooming +// +// Passed nothing, returns nothing +// +static void AM_changeWindowScale(void) +{ + if (movement_smooth) + { + float f_paninc = (float)F_PANINC / (float)FRACUNIT * (float)tic_vars.frac; + + if (f_paninc < 0.01f) + f_paninc = 0.01f; + + scale_mtof = prev_scale_mtof; + if (curr_mtof_zoommul == M_ZOOMIN) + { + mtof_zoommul = ((int) ((float)FRACUNIT * (1.00f + f_paninc / 200.0f))); + ftom_zoommul = ((int) ((float)FRACUNIT / (1.00f + f_paninc / 200.0f))); + } + if (curr_mtof_zoommul == M_ZOOMOUT) + { + mtof_zoommul = ((int) ((float)FRACUNIT / (1.00f + f_paninc / 200.0f))); + ftom_zoommul = ((int) ((float)FRACUNIT * (1.00f + f_paninc / 200.0f))); + } + } + + scale_mtof = FixedMul(scale_mtof, mtof_zoommul); + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + + if (scale_mtof < min_scale_mtof) + AM_minOutWindowScale(); + else if (scale_mtof > max_scale_mtof) + AM_maxOutWindowScale(); + else + AM_activateNewScale(); +} + +// +// AM_doFollowPlayer() +// +// Turn on follow mode - the map scrolls opposite to player motion +// +// Passed nothing, returns nothing +// +static void AM_doFollowPlayer(void) +{ + m_x = (viewx >> FRACTOMAPBITS) - m_w/2; + m_y = (viewy >> FRACTOMAPBITS) - m_h/2; + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; +} + +// +// AM_Ticker() +// +// Updates on gametic - enter follow mode, zoom, or change map location +// +// Passed nothing, returns nothing +// +void AM_Ticker (void) +{ + prev_scale_mtof = scale_mtof; + prev_m_x = m_x; + prev_m_y = m_y; +} + +// +// AM_clipMline() +// +// 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. +// +// Passed the line's coordinates on map and in the frame buffer performs +// clipping on them in the lines frame coordinates. +// Returns true if any part of line was not clipped +// +static dboolean AM_clipMline +( mline_t* ml, + fline_t* fl ) +{ + enum + { + LEFT =1, + RIGHT =2, + BOTTOM =4, + TOP =8 + }; + + register int outcode1 = 0; + register int outcode2 = 0; + register int outside; + + fpoint_t tmp; + int dx; + int dy; + float dx_f, dy_f; + + +#define DOOUTCODE(oc, mx, my) \ + (oc) = 0; \ + if ((my) < f_y) (oc) |= TOP; \ + else if ((my) >= f_y + f_h) (oc) |= BOTTOM; \ + if ((mx) < f_x) (oc) |= LEFT; \ + else if ((mx) >= f_x + 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; + + if (am_frame.precise) + { + fl->a.fx = CXMTOF_F(ml->a.fx); + fl->a.fy = CYMTOF_F(ml->a.fy); + fl->b.fx = CXMTOF_F(ml->b.fx); + fl->b.fy = CYMTOF_F(ml->b.fy); + } + + 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; + // 'int64' math to avoid overflows on long lines + tmp.x = fl->a.x + (fixed_t)(((int_64_t)dx*(fl->a.y-f_y))/dy); + tmp.y = f_y; + if (am_frame.precise) + { + dy_f = fl->a.fy - fl->b.fy; + dx_f = fl->b.fx - fl->a.fx; + tmp.fx = fl->a.fx + (dx_f*(fl->a.fy-f_y))/dy_f; + tmp.fy = (float)f_y; + } + } + else if (outside & BOTTOM) + { + dy = fl->a.y - fl->b.y; + dx = fl->b.x - fl->a.x; + tmp.x = fl->a.x + (fixed_t)(((int_64_t)dx*(fl->a.y-(f_y+f_h)))/dy); + tmp.y = f_y+f_h-1; + if (am_frame.precise) + { + dy_f = fl->a.fy - fl->b.fy; + dx_f = fl->b.fx - fl->a.fx; + tmp.fx = fl->a.fx + (dx_f*(fl->a.fy-(f_y+f_h)))/dy_f; + tmp.fy = (float)(f_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 + (fixed_t)(((int_64_t)dy*(f_x+f_w-1 - fl->a.x))/dx); + tmp.x = f_x+f_w-1; + if (am_frame.precise) + { + dy_f = fl->b.fy - fl->a.fy; + dx_f = fl->b.fx - fl->a.fx; + tmp.fy = fl->a.fy + (dy_f*(f_x+f_w-1 - fl->a.fx))/dx_f; + tmp.fx = (float)(f_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 + (fixed_t)(((int_64_t)dy*(f_x-fl->a.x))/dx); + tmp.x = f_x; + if (am_frame.precise) + { + dy_f = fl->b.fy - fl->a.fy; + dx_f = fl->b.fx - fl->a.fx; + tmp.fy = fl->a.fy + (dy_f*(f_x-fl->a.fx))/dx_f; + tmp.fx = (float)f_x; + } + } + + 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 + +// +// AM_drawMline() +// +// Clip lines, draw visible parts of lines. +// +// Passed the map coordinates of the line, and the color to draw it +// Color -1 is special and prevents drawing. Color 247 is special and +// is translated to black, allowing Color 0 to represent feature disable +// in the defaults file. +// Returns nothing. +// +static void AM_drawMline +( mline_t* ml, + int color ) +{ + static fline_t fl; + + if (color==-1) // jff 4/3/98 allow not drawing any sort of line + return; // by setting its color to -1 + if (color==247) // jff 4/3/98 if color is 247 (xparent), use black + color=0; + + if (AM_clipMline(ml, &fl)) + { + // draws it on frame buffer using fb coords + if (map_use_multisamling) + V_DrawLineWu(&fl, color); + else + V_DrawLine(&fl, color); + } +} + +// +// AM_drawGrid() +// +// Draws blockmap aligned grid lines. +// +// Passed the color to draw the grid lines +// Returns nothing +// +static void AM_drawGrid(int color) +{ + fixed_t x, y; + fixed_t start, end; + mline_t ml; + fixed_t minlen, extx, exty; + fixed_t minx, miny; + fixed_t gridsize = map_grid_size << MAPBITS; + + if(map_grid_size == -1) + { + fixed_t oprtimal_gridsize = m_h / 16; + gridsize = 8; + while (gridsize < oprtimal_gridsize) + gridsize <<= 1; + if (gridsize - oprtimal_gridsize > oprtimal_gridsize - (gridsize >> 1)) + gridsize >>= 1; + } + + // [RH] Calculate a minimum for how long the grid lines should be so that + // they cover the screen at any rotation. + minlen = M_DoubleToInt(sqrt ((float)m_w*(float)m_w + (float)m_h*(float)m_h)); + extx = (minlen - m_w) / 2; + exty = (minlen - m_h) / 2; + + minx = m_x; + miny = m_y; + + // Fix vanilla automap grid bug: losing grid lines near the map boundary + // due to unnecessary addition of MAPBLOCKUNITS to start + // Proper math is to just subtract the remainder; AM_drawMLine will take care + // of clipping if an extra line is offscreen. + + // Figure out start of vertical gridlines + start = minx - extx; + if ((start - (bmaporgx>>FRACTOMAPBITS)) % gridsize) + start -= ((start - (bmaporgx>>FRACTOMAPBITS)) % gridsize); + end = minx + minlen - extx; + + // draw vertical gridlines + for (x = start; x < end; x += gridsize) + { + ml.a.x = x; + ml.b.x = x; + ml.a.y = miny - exty; + ml.b.y = ml.a.y + minlen; + if (automapmode & am_rotate) + { + AM_rotatePoint (&ml.a); + AM_rotatePoint (&ml.b); + } + else + { + AM_SetMPointFloatValue(&ml.a); + AM_SetMPointFloatValue(&ml.b); + } + AM_drawMline(&ml, color); + } + + // Figure out start of horizontal gridlines + start = miny - exty; + if ((start - (bmaporgy>>FRACTOMAPBITS)) % gridsize) + start -= ((start - (bmaporgy>>FRACTOMAPBITS)) % gridsize); + end = miny + minlen - exty; + + // draw horizontal gridlines + for (y = start; y < end; y += gridsize) + { + ml.a.x = minx - extx; + ml.b.x = ml.a.x + minlen; + ml.a.y = y; + ml.b.y = y; + if (automapmode & am_rotate) + { + AM_rotatePoint (&ml.a); + AM_rotatePoint (&ml.b); + } + else + { + AM_SetMPointFloatValue(&ml.a); + AM_SetMPointFloatValue(&ml.b); + } + AM_drawMline(&ml, color); + } +} + +// +// AM_DoorColor() +// +// Returns the 'color' or key needed for a door linedef type +// +// Passed the type of linedef, returns: +// -1 if not a keyed door +// 0 if a red key required +// 1 if a blue key required +// 2 if a yellow key required +// 3 if a multiple keys required +// +// jff 4/3/98 add routine to get color of generalized keyed door +// +static int AM_DoorColor(int type) +{ + if (GenLockedBase <= type && type< GenDoorBase) + { + type -= GenLockedBase; + type = (type & LockedKey) >> LockedKeyShift; + if (!type || type==7) + return 3; //any or all keys + else return (type-1)%3; + } + switch (type) // closed keyed door + { + case 26: case 32: case 99: case 133: + /*bluekey*/ + return 1; + case 27: case 34: case 136: case 137: + /*yellowkey*/ + return 2; + case 28: case 33: case 134: case 135: + /*redkey*/ + return 0; + default: + return -1; //not a keyed door + } +} + +// +// Determines visible lines, draws them. +// This is LineDef based, not LineSeg based. +// +// jff 1/5/98 many changes in this routine +// backward compatibility not needed, so just changes, no ifs +// addition of clauses for: +// doors opening, keyed door id, secret sectors, +// teleports, exit lines, key things +// ability to suppress any of added features or lines with no height changes +// +// support for gamma correction in automap abandoned +// +// jff 4/3/98 changed mapcolor_xxxx=0 as control to disable feature +// jff 4/3/98 changed mapcolor_xxxx=-1 to disable drawing line completely +// +static void AM_drawWalls(void) +{ + int i; + static mline_t l; + + // draw the unclipped visible portions of all lines + for (i=0;i> FRACTOMAPBITS > am_frame.bbox[BOXRIGHT] || + lines[i].bbox[BOXRIGHT] >> FRACTOMAPBITS < am_frame.bbox[BOXLEFT] || + lines[i].bbox[BOXBOTTOM] >> FRACTOMAPBITS > am_frame.bbox[BOXTOP] || + lines[i].bbox[BOXTOP] >> FRACTOMAPBITS < am_frame.bbox[BOXBOTTOM]) + { + continue; + } + + l.a.x = lines[i].v1->x >> FRACTOMAPBITS; + l.a.y = lines[i].v1->y >> FRACTOMAPBITS; + l.b.x = lines[i].v2->x >> FRACTOMAPBITS; + l.b.y = lines[i].v2->y >> FRACTOMAPBITS; + + if (automapmode & am_rotate) + { + AM_rotatePoint(&l.a); + AM_rotatePoint(&l.b); + } + else + { + AM_SetMPointFloatValue(&l.a); + AM_SetMPointFloatValue(&l.b); + } + + // if line has been seen or IDDT has been used + if (ddt_cheating || (lines[i].flags & ML_MAPPED)) + { + if ((lines[i].flags & ML_DONTDRAW) && !ddt_cheating) + continue; + { + /* cph - show keyed doors and lines */ + int amd; + if ((mapcolor_bdor || mapcolor_ydor || mapcolor_rdor) && + !(lines[i].flags & ML_SECRET) && /* non-secret */ + (amd = AM_DoorColor(lines[i].special)) != -1 + ) + { + { + switch (amd) /* closed keyed door */ + { + case 1: + /*bluekey*/ + AM_drawMline(&l, + mapcolor_bdor? mapcolor_bdor : mapcolor_cchg); + continue; + case 2: + /*yellowkey*/ + AM_drawMline(&l, + mapcolor_ydor? mapcolor_ydor : mapcolor_cchg); + continue; + case 0: + /*redkey*/ + AM_drawMline(&l, + mapcolor_rdor? mapcolor_rdor : mapcolor_cchg); + continue; + case 3: + /*any or all*/ + AM_drawMline(&l, + mapcolor_clsd? mapcolor_clsd : mapcolor_cchg); + continue; + } + } + } + } + if /* jff 4/23/98 add exit lines to automap */ + ( + mapcolor_exit && + ( + lines[i].special==11 || + lines[i].special==52 || + lines[i].special==197 || + lines[i].special==51 || + lines[i].special==124 || + lines[i].special==198 + ) + ) { + AM_drawMline(&l, mapcolor_exit); /* exit line */ + continue; + } + + if (!lines[i].backsector) + { + // jff 1/10/98 add new color for 1S secret sector boundary + if (mapcolor_secr && //jff 4/3/98 0 is disable + ( + ( + map_secret_after && + P_WasSecret(lines[i].frontsector) && + !P_IsSecret(lines[i].frontsector) + ) + || + ( + !map_secret_after && + P_WasSecret(lines[i].frontsector) + ) + ) + ) + AM_drawMline(&l, mapcolor_secr); // line bounding secret sector + else //jff 2/16/98 fixed bug + AM_drawMline(&l, mapcolor_wall); // special was cleared + } + else /* now for 2S lines */ + { + // jff 1/10/98 add color change for all teleporter types + if + ( + mapcolor_tele && !(lines[i].flags & ML_SECRET) && + (lines[i].special == 39 || lines[i].special == 97 || + lines[i].special == 125 || lines[i].special == 126) + ) + { // teleporters + AM_drawMline(&l, mapcolor_tele); + } + else if (lines[i].flags & ML_SECRET) // secret door + { + AM_drawMline(&l, mapcolor_wall); // wall color + } + else if + ( + mapcolor_clsd && + !(lines[i].flags & ML_SECRET) && // non-secret closed door + ((lines[i].backsector->floorheight==lines[i].backsector->ceilingheight) || + (lines[i].frontsector->floorheight==lines[i].frontsector->ceilingheight)) + ) + { + AM_drawMline(&l, mapcolor_clsd); // non-secret closed door + } //jff 1/6/98 show secret sector 2S lines + else if + ( + mapcolor_secr && //jff 2/16/98 fixed bug + ( // special was cleared after getting it + (map_secret_after && + ( + (P_WasSecret(lines[i].frontsector) + && !P_IsSecret(lines[i].frontsector)) || + (P_WasSecret(lines[i].backsector) + && !P_IsSecret(lines[i].backsector)) + ) + ) + || //jff 3/9/98 add logic to not show secret til after entered + ( // if map_secret_after is true + !map_secret_after && + (P_WasSecret(lines[i].frontsector) || + P_WasSecret(lines[i].backsector)) + ) + ) + ) + { + AM_drawMline(&l, mapcolor_secr); // line bounding secret sector + } //jff 1/6/98 end secret sector line change + else if (lines[i].backsector->floorheight != + lines[i].frontsector->floorheight) + { + AM_drawMline(&l, mapcolor_fchg); // floor level change + } + else if (lines[i].backsector->ceilingheight != + lines[i].frontsector->ceilingheight) + { + AM_drawMline(&l, mapcolor_cchg); // ceiling level change + } + else if (mapcolor_flat && ddt_cheating) + { + AM_drawMline(&l, mapcolor_flat); //2S lines that appear only in IDDT + } + } + } // now draw the lines only visible because the player has computermap + else if (plr->powers[pw_allmap]) // computermap visible lines + { + if (!(lines[i].flags & ML_DONTDRAW)) // invisible flag lines do not show + { + if + ( + mapcolor_flat + || + !lines[i].backsector + || + lines[i].backsector->floorheight + != lines[i].frontsector->floorheight + || + lines[i].backsector->ceilingheight + != lines[i].frontsector->ceilingheight + ) + AM_drawMline(&l, mapcolor_unsn); + } + } + } +} + +// +// AM_drawLineCharacter() +// +// Draws a vector graphic according to numerous parameters +// +// Passed the structure defining the vector graphic shape, the number +// of vectors in it, the scale to draw it at, the angle to draw it at, +// the color to draw it with, and the map coordinates to draw it at. +// Returns nothing +// +static void AM_drawLineCharacter +( mline_t* lineguy, + int lineguylines, + fixed_t scale, + angle_t angle, + int color, + fixed_t x, + fixed_t y ) +{ + int i; + mline_t l; + + if (automapmode & am_rotate) angle -= viewangle - ANG90; // cph + + for (i=0;ix = mo->PrevX + FixedMul(tic_vars.frac, mo->x - mo->PrevX); + p->y = mo->PrevY + FixedMul(tic_vars.frac, mo->y - mo->PrevY); + if (mo->player) + *angle = mo->player->prev_viewangle + FixedMul(tic_vars.frac, R_SmoothPlaying_Get(mo->player) - mo->player->prev_viewangle); + else + *angle = mo->angle; + } + else + { + p->x = mo->x; + p->y = mo->y; + *angle = mo->angle; + } + + p->x = p->x >> FRACTOMAPBITS; + p->y = p->y >> FRACTOMAPBITS; +} + +// +// AM_drawPlayers() +// +// Draws the player arrow in single player, +// or all the player arrows in a netgame. +// +// Passed nothing, returns nothing +// +static void AM_drawPlayers(void) +{ + int i; + angle_t angle; + mpoint_t pt; + fixed_t scale; + +#if defined(HAVE_LIBSDL2_IMAGE) && defined(GL_DOOM) + if (V_GetMode() == VID_MODEGL) + { + if (map_things_appearance == map_things_appearance_icon) + return; + } +#endif + + if (map_things_appearance == map_things_appearance_scaled) + scale = (BETWEEN(4<mo->radius)>>FRACTOMAPBITS); + else + scale = 16<> FRACTOMAPBITS; + pt.y = viewy >> FRACTOMAPBITS; + if (automapmode & am_rotate) + AM_rotatePoint(&pt); + else + AM_SetMPointFloatValue(&pt); + + if (ddt_cheating) + AM_drawLineCharacter(cheat_player_arrow, NUMCHEATPLYRLINES, scale, viewangle, mapcolor_sngl, pt.x, pt.y); + else + AM_drawLineCharacter(player_arrow, NUMPLYRLINES, scale, viewangle, mapcolor_sngl, pt.x, pt.y); + return; + } + + for (i=0;imo, &pt, &angle); + + if (automapmode & am_rotate) + AM_rotatePoint(&pt); + else + AM_SetMPointFloatValue(&pt); + + AM_drawLineCharacter (player_arrow, NUMPLYRLINES, scale, angle, + p->powers[pw_invisibility] ? 246 /* *close* to black */ + : mapcolor_plyr[i], //jff 1/6/98 use default color + pt.x, pt.y); + } + } +} + +static void AM_ProcessNiceThing(mobj_t* mobj, angle_t angle, fixed_t x, fixed_t y) +{ +#ifdef GL_DOOM + const float shadow_scale_factor = 1.3f; + angle_t ang; + int i, type, radius, rotate, need_shadow; + float fx, fy, fradius, rot, shadow_radius; + unsigned char r, g, b, a; + + typedef struct map_nice_icon_param_s + { + spritenum_t sprite; + int icon; + int radius; + int rotate; + unsigned char r, g, b; + } map_nice_icon_param_t; + + static const map_nice_icon_param_t icons[] = + { + {SPR_STIM, am_icon_health, 12, 0, 100, 100, 200}, + {SPR_MEDI, am_icon_health, 16, 0, 100, 100, 200}, + {SPR_BON1, am_icon_health, 10, 0, 0, 0, 200}, + + {SPR_BON2, am_icon_armor, 10, 0, 0, 200, 0}, + {SPR_ARM1, am_icon_armor, 16, 0, 100, 200, 100}, + {SPR_ARM2, am_icon_armor, 16, 0, 100, 100, 200}, + + {SPR_CLIP, am_icon_ammo, 10, 0, 180, 150, 50}, + {SPR_AMMO, am_icon_ammo, 16, 0, 180, 150, 50}, + {SPR_ROCK, am_icon_ammo, 10, 0, 180, 150, 50}, + {SPR_BROK, am_icon_ammo, 16, 0, 180, 150, 50}, + + {SPR_CELL, am_icon_ammo, 10, 0, 180, 150, 50}, + {SPR_CELP, am_icon_ammo, 16, 0, 180, 150, 50}, + {SPR_SHEL, am_icon_ammo, 10, 0, 180, 150, 50}, + {SPR_SBOX, am_icon_ammo, 16, 0, 180, 150, 50}, + {SPR_BPAK, am_icon_ammo, 16, 0, 180, 150, 50}, + + {SPR_BKEY, am_icon_key, 10, 0, 0, 0, 255}, + {SPR_BSKU, am_icon_key, 10, 0, 0, 0, 255}, + {SPR_YKEY, am_icon_key, 10, 0, 255, 255, 0}, + {SPR_YSKU, am_icon_key, 10, 0, 255, 255, 0}, + {SPR_RKEY, am_icon_key, 10, 0, 255, 0, 0}, + {SPR_RSKU, am_icon_key, 10, 0, 255, 0, 0}, + + {SPR_PINV, am_icon_power, 16, 0, 220, 100, 220}, + {SPR_PSTR, am_icon_power, 16, 0, 220, 100, 220}, + {SPR_PINS, am_icon_power, 16, 0, 220, 100, 220}, + {SPR_SUIT, am_icon_power, 16, 0, 220, 100, 220}, + {SPR_PMAP, am_icon_power, 16, 0, 220, 100, 220}, + {SPR_PVIS, am_icon_power, 16, 0, 220, 100, 220}, + {SPR_SOUL, am_icon_power, 16, 0, 220, 100, 220}, + {SPR_MEGA, am_icon_power, 16, 0, 220, 100, 220}, + + {SPR_BFUG, am_icon_weap, 20, 0, 220, 180, 100}, + {SPR_MGUN, am_icon_weap, 20, 0, 220, 180, 100}, + {SPR_CSAW, am_icon_weap, 20, 0, 220, 180, 100}, + {SPR_LAUN, am_icon_weap, 20, 0, 220, 180, 100}, + {SPR_PLAS, am_icon_weap, 20, 0, 220, 180, 100}, + {SPR_SHOT, am_icon_weap, 20, 0, 220, 180, 100}, + {SPR_SGN2, am_icon_weap, 20, 0, 220, 180, 100}, + + {SPR_BLUD, am_icon_bullet, 8, 0, 255, 0, 0}, + {SPR_PUFF, am_icon_bullet, 8, 0, 255, 255, 115}, + {SPR_MISL, am_icon_bullet, 8, 0, 91, 71, 43}, + {SPR_PLSS, am_icon_bullet, 8, 0, 115, 115, 255}, + {SPR_PLSE, am_icon_bullet, 8, 0, 115, 115, 255}, + {SPR_BFS1, am_icon_bullet, 12, 0, 119, 255, 111}, + {SPR_BFE1, am_icon_bullet, 12, 0, 119, 255, 111}, + + {NUMSPRITES} + }; + + need_shadow = true; + + type = am_icon_normal; + r = 220; + g = 180; + b = 100; + a = 255; + radius = mobj->radius; + rotate = true; + + if (mobj->player) + { + player_t *p = mobj->player; + int color = mapcolor_plyr[p - players]; + const unsigned char *playpal = V_GetPlaypal(); + + if ((deathmatch && !demoplayback) && p != plr) + return; + + type = am_icon_player; + + r = playpal[3 * color + 0]; + g = playpal[3 * color + 1]; + b = playpal[3 * color + 2]; + a = p->powers[pw_invisibility] ? 128 : 255; + + radius = mobj->radius; + rotate = true; + } + else if (mobj->flags & MF_COUNTKILL) + { + if (mobj->flags & MF_CORPSE) + { + need_shadow = false; + type = am_icon_corpse; + r = 120; + a = 128; + } + else + { + type = am_icon_monster; + r = 200; + } + g = 0; + b = 0; + radius = BETWEEN(4<radius); + rotate = true; + } + else + { + i = 0; + while (icons[i].sprite < NUMSPRITES) + { + if (mobj->sprite == icons[i].sprite) + { + type = icons[i].icon; + r = icons[i].r; + g = icons[i].g; + b = icons[i].b; + radius = icons[i].radius << 16; + rotate = icons[i].rotate; + + break; + } + i++; + } + } + + fradius = MTOF_F(radius >> FRACTOMAPBITS); + if (fradius < 1.0f) + return; + if (fradius < 4.0f) + need_shadow = false; + + fx = CXMTOF_F(x); + fy = CYMTOF_F(y); + + shadow_radius = fradius * shadow_scale_factor; + if (fx + shadow_radius < 0 || + fx - shadow_radius > (float)SCREENWIDTH || + fy + shadow_radius < 0 || + fy - shadow_radius > (float)SCREENHEIGHT) + { + return; + } + + ang = (rotate ? angle : 0) + ((automapmode & am_rotate) ? ANG90 - viewangle : 0); + rot = -(float)ang / (float)(1u << 31) * (float)M_PI; + + gld_AddNiceThing(type, fx, fy, fradius, rot, r, g, b, a); + if (need_shadow) + { + gld_AddNiceThing(am_icon_shadow, fx, fy, shadow_radius, rot, 0, 0, 0, 128); + } +#endif +} + +static void AM_DrawNiceThings(void) +{ +#ifdef GL_DOOM + int i; + mobj_t* t; + mpoint_t p; + angle_t angle; + + gld_ClearNiceThings(); + + // draw players + for (i = 0; i < MAXPLAYERS; i++) + { + if ((deathmatch && !demoplayback) && &players[i] != plr) + continue; + + if (playeringame[i]) + { + t = players[i].mo; + AM_GetMobjPosition(t, &p, &angle); + if (automapmode & am_rotate) + AM_rotatePoint(&p); + else + AM_SetMPointFloatValue(&p); + AM_ProcessNiceThing(t, angle, p.x, p.y); + } + } + + // walls + if (ddt_cheating == 2) + { + // for all sectors + for (i = 0; i < numsectors; i++) + { + if (!(players[displayplayer].cheats & CF_NOCLIP) && + (sectors[i].bbox[BOXLEFT] > am_frame.bbox[BOXRIGHT] || + sectors[i].bbox[BOXRIGHT] < am_frame.bbox[BOXLEFT] || + sectors[i].bbox[BOXBOTTOM] > am_frame.bbox[BOXTOP] || + sectors[i].bbox[BOXTOP] < am_frame.bbox[BOXBOTTOM])) + { + continue; + } + + t = sectors[i].thinglist; + while (t) // for all things in that sector + { + if (!t->player) + { + AM_GetMobjPosition(t, &p, &angle); + if (automapmode & am_rotate) + AM_rotatePoint(&p); + AM_ProcessNiceThing(t, angle, p.x, p.y); + } + t = t->snext; + } + } + } + + // marked locations on the automap + { + float radius; + int anim_flash_level = (gametic % 32); + + // Flashing animation for hilight + // Pulsates between 0.5-1.0f (multiplied with hilight alpha) + if (anim_flash_level >= 16) + { + anim_flash_level = 32 - anim_flash_level; + } + anim_flash_level = 127 + anim_flash_level * 8; + + // do not want to have too small marks + radius = MTOF_F(16 << MAPBITS); + radius = BETWEEN(8.0f, 128.0f, radius); + + for (i = 0; i < markpointnum; i++) // killough 2/22/98: remove automap mark limit + { + if (markpoints[i].x != -1) + { + mpoint_t p; + + p.x = markpoints[i].x; + p.y = markpoints[i].y; + + if (automapmode & am_rotate) + AM_rotatePoint(&p); + else + AM_SetMPointFloatValue(&p); + + p.fx = CXMTOF_F(p.fx); + p.fy = CYMTOF_F(p.fy); + + gld_AddNiceThing(am_icon_mark, p.fx, p.fy, radius, 0, 255, 255, 0, (unsigned char)anim_flash_level); + } + } + } + +#endif +} + +// +// AM_drawThings() +// +// Draws the things on the automap in double IDDT cheat mode +// +// Passed colors and colorrange, no longer used +// Returns nothing +// +static void AM_drawThings(void) +{ + int i; + mobj_t* t; + +#if defined(HAVE_LIBSDL2_IMAGE) && defined(GL_DOOM) + if (V_GetMode() == VID_MODEGL) + { + if (map_things_appearance == map_things_appearance_icon) + { + AM_DrawNiceThings(); + return; + } + } +#endif + + if (ddt_cheating != 2) + return; + + // for all sectors + for (i=0;i am_frame.bbox[BOXRIGHT] || + sectors[i].bbox[BOXRIGHT] < am_frame.bbox[BOXLEFT] || + sectors[i].bbox[BOXBOTTOM] > am_frame.bbox[BOXTOP] || + sectors[i].bbox[BOXTOP] < am_frame.bbox[BOXBOTTOM])) + { + continue; + } + + for (pass = 0; pass < 2; pass += (enemies ? 1 : 2)) + { + t = sectors[i].thinglist; + while (t) // for all things in that sector + { + mpoint_t p; + angle_t angle; + fixed_t scale; + + //e6y: stop if all enemies from current sector already has been drawn + if (pass == 1 && enemies == 0) + break; + if (pass == ((t->flags & (MF_COUNTKILL | MF_CORPSE)) == MF_COUNTKILL ? + (pass == 0 ? enemies++: enemies--), 0 : 1)) + { + t = t->snext; + continue; + } + + if (map_things_appearance == map_things_appearance_scaled) + scale = (BETWEEN(4<radius)>>FRACTOMAPBITS);// * 16 / 20; + else + scale = 16<info->doomednum) + { + //jff 1/5/98 treat keys special + case 38: case 13: //jff red key + color = mapcolor_rkey != -1? mapcolor_rkey : mapcolor_sprt; break; + case 39: case 6: //jff yellow key + color = mapcolor_ykey != -1? mapcolor_ykey : mapcolor_sprt; break; + case 40: case 5: //jff blue key + color = mapcolor_bkey != -1? mapcolor_bkey : mapcolor_sprt; break; + } + + if (color != -1) + { + AM_drawLineCharacter(cross_mark, NUMCROSSMARKLINES, + scale, t->angle, color, p.x, p.y); + t = t->snext; + continue; + } + } + //jff 1/5/98 end added code for keys + //jff previously entire code + AM_drawLineCharacter(thintriangle_guy, NUMTHINTRIANGLEGUYLINES, + scale, angle, + t->flags & MF_FRIEND && !t->player ? mapcolor_frnd : + /* cph 2006/07/30 - Show count-as-kills in red. */ + ((t->flags & (MF_COUNTKILL | MF_CORPSE)) == MF_COUNTKILL) ? mapcolor_enemy : + /* bbm 2/28/03 Show countable items in yellow. */ + t->flags & MF_COUNTITEM ? mapcolor_item : mapcolor_sprt, + p.x, p.y); + t = t->snext; + } + } + } +} + +// +// AM_drawMarks() +// +// Draw the marked locations on the automap +// +// Passed nothing, returns nothing +// +// killough 2/22/98: +// Rewrote AM_drawMarks(). Removed limit on marks. +// +static void AM_drawMarks(void) +{ + int i; + char namebuf[16] = "AMMNUM0"; + +#if defined(HAVE_LIBSDL2_IMAGE) && defined(GL_DOOM) + if (V_GetMode() == VID_MODEGL) + { + if (map_things_appearance == map_things_appearance_icon) + return; + } +#endif + + for (i = 0; i < markpointnum; i++) // killough 2/22/98: remove automap mark limit + { + if (markpoints[i].x != -1) + { + int k, w; + mpoint_t p; + + p.x = markpoints[i].x;// - m_x + prev_m_x; + p.y = markpoints[i].y;// - m_y + prev_m_y; + + if (automapmode & am_rotate) + AM_rotatePoint(&p); + else + AM_SetMPointFloatValue(&p); + + p.x = CXMTOF(p.x) - markpoints[i].w * SCREENWIDTH / 320 / 2; + p.y = CYMTOF(p.y) - markpoints[i].h * SCREENHEIGHT / 200 / 2; + if (am_frame.precise) + { + p.fx = CXMTOF_F(p.fx) - (float)markpoints[i].w * SCREENWIDTH / 320.0f / 2.0f; + p.fy = CYMTOF_F(p.fy) - (float)markpoints[i].h * SCREENHEIGHT / 200.0f / 2.0f; + } + + if (V_GetMode() == VID_MODEGL ? + p.y < f_y + f_h && p.y + markpoints[i].h * SCREENHEIGHT / 200 >= f_y : + p.y < f_y + f_h && p.y >= f_y) + { + w = 0; + for (k = 0; k < (int)strlen(markpoints[i].label); k++) + { + namebuf[6] = markpoints[i].label[k]; + + if (p.x < f_x + f_w && + p.x + markpoints[i].widths[k] * SCREENWIDTH / 320 >= f_x) + { + float fx, fy; + int x, y, flags; + + switch (render_stretch_hud) + { + default: + case patch_stretch_16x10: + fx = (float)p.fx / patches_scalex; + fy = (float)p.fy * 200.0f / SCREENHEIGHT; + + x = p.x / patches_scalex; + y = p.y * 200 / SCREENHEIGHT; + + flags = VPT_ALIGN_LEFT | VPT_STRETCH; + break; + case patch_stretch_4x3: + fx = (float)p.fx * 320.0f / WIDE_SCREENWIDTH; + fy = (float)p.fy * 200.0f / WIDE_SCREENHEIGHT; + + x = p.x * 320 / WIDE_SCREENWIDTH; + y = p.y * 200 / WIDE_SCREENHEIGHT; + + flags = VPT_ALIGN_LEFT | VPT_STRETCH; + break; + case patch_stretch_full: + fx = (float)p.fx * 320.0f / SCREENWIDTH; + fy = (float)p.fy * 200.0f / SCREENHEIGHT; + + x = p.x * 320 / SCREENWIDTH; + y = p.y * 200 / SCREENHEIGHT; + + flags = VPT_ALIGN_WIDE | VPT_STRETCH; + break; + } + + if (am_frame.precise) + { + V_DrawNamePatchPrecise(fx, fy, FB, namebuf, CR_DEFAULT, flags); + } + else + { + V_DrawNamePatch(x, y, FB, namebuf, CR_DEFAULT, flags); + } + } + + w += markpoints[i].widths[k] + 1; + p.x += w * SCREENWIDTH / 320; + if (am_frame.precise) + { + p.fx += (float)w * SCREENWIDTH / 320.0f; + } + } + } + } + } +} + +// +// AM_drawCrosshair() +// +// Draw the single point crosshair representing map center +// +// Passed the color to draw the pixel with +// Returns nothing +// +// CPhipps - made static inline, and use the general pixel plotter function + +static void AM_drawCrosshair(int color) +{ + fline_t line; + + line.a.x = f_x+(f_w/2)-1; + line.a.y = f_y+(f_h/2); + line.b.x = f_x+(f_w/2)+1; + line.b.y = f_y+(f_h/2); + AM_SetFPointFloatValue(&line.a); + AM_SetFPointFloatValue(&line.b); + V_DrawLine(&line, color); + + line.a.x = f_x+(f_w/2); + line.a.y = f_y+(f_h/2)-1; + line.b.x = f_x+(f_w/2); + line.b.y = f_y+(f_h/2)+1; + AM_SetFPointFloatValue(&line.a); + AM_SetFPointFloatValue(&line.b); + V_DrawLine(&line, color); +} + +void M_ChangeMapGridSize(void) +{ + if (map_grid_size > 0) + { + map_grid_size = MAX(map_grid_size, 8); + } +} + +void M_ChangeMapTextured(void) +{ +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + gld_ProcessTexturedMap(); + } +#endif +} + +void M_ChangeMapMultisamling(void) +{ + if (map_use_multisamling && V_GetMode() != VID_MODEGL) + { + V_InitFlexTranTable(); + } +} + +//============================================================================= +// +// AM_drawSubsectors +// +//============================================================================= + +void AM_drawSubsectors(void) +{ +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + gld_MapDrawSubsectors(plr, f_x, f_y, m_x, m_y, f_w, f_h, scale_mtof); + } +#endif +} + +static void AM_setFrameVariables(void) +{ + float angle; + + angle = (float)(ANG90 - viewangle) / (float)(1u << 31) * (float)M_PI; + am_frame.sin_f = (float)sin(angle); + am_frame.cos_f = (float)cos(angle); + am_frame.sin = finesine[(ANG90 - viewangle)>>ANGLETOFINESHIFT]; + am_frame.cos = finecosine[(ANG90 - viewangle)>>ANGLETOFINESHIFT]; + + am_frame.centerx = m_x + m_w / 2; + am_frame.centery = m_y + m_h / 2; + am_frame.centerx_f = (float)m_x + (float)m_w / 2.0f; + am_frame.centery_f = (float)m_y + (float)m_h / 2.0f; + + if (automapmode & am_rotate) + { + float dx = (float)(m_x2 - am_frame.centerx); + float dy = (float)(m_y2 - am_frame.centery); + fixed_t r = M_DoubleToInt(sqrt(dx * dx + dy * dy)); + + am_frame.bbox[BOXLEFT] = am_frame.centerx - r; + am_frame.bbox[BOXRIGHT] = am_frame.centerx + r; + am_frame.bbox[BOXBOTTOM] = am_frame.centery - r; + am_frame.bbox[BOXTOP] = am_frame.centery + r; + } + else + { + am_frame.bbox[BOXLEFT] = m_x; + am_frame.bbox[BOXRIGHT] = m_x2; + am_frame.bbox[BOXBOTTOM] = m_y; + am_frame.bbox[BOXTOP] = m_y2; + } + + am_frame.precise = (V_GetMode() == VID_MODEGL); +} + +// +// AM_Drawer() +// +// Draws the entire automap +// +// Passed nothing, returns nothing +// + +void AM_Drawer (void) +{ + // CPhipps - all automap modes put into one enum + if (!(automapmode & am_active)) + return; + + if (automapmode & am_follow) + AM_doFollowPlayer(); + + // Change the zoom if necessary + if (ftom_zoommul != FRACUNIT) + AM_changeWindowScale(); + + // Change x,y location + if (m_paninc.x || m_paninc.y) + AM_changeWindowLoc(); + + AM_setFrameVariables(); + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + // do not use multisampling in automap mode if map_use_multisamling 0 + gld_MultisamplingSet(); + } +#endif + + if (!(automapmode & am_overlay)) // cph - If not overlay mode, clear background for the automap + V_FillRect(FB, f_x, f_y, f_w, f_h, (byte)mapcolor_back); //jff 1/5/98 background default color + + if (map_textured) + { + AM_drawSubsectors(); + } + + if (automapmode & am_grid) + AM_drawGrid(mapcolor_grid); //jff 1/7/98 grid default color + AM_drawWalls(); + AM_drawPlayers(); + AM_drawThings(); //jff 1/5/98 default double IDDT sprite + AM_drawCrosshair(mapcolor_hair); //jff 1/7/98 default crosshair color + +#if defined(GL_DOOM) + if (V_GetMode() == VID_MODEGL) + { + gld_DrawMapLines(); + M_ArrayClear(&map_lines); + +#if defined(HAVE_LIBSDL2_IMAGE) + if (map_things_appearance == map_things_appearance_icon) + { + gld_DrawNiceThings(f_x, f_y, f_w, f_h); + } +#endif + } +#endif + + AM_drawMarks(); +} diff --git a/src/am_map.h b/src/am_map.h new file mode 100644 index 0000000..8ca5c11 --- /dev/null +++ b/src/am_map.h @@ -0,0 +1,189 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * AutoMap module. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __AMMAP_H__ +#define __AMMAP_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "d_event.h" +#include "m_misc.h" + +typedef struct map_point_s +{ + float x, y; + unsigned char r, g, b, a; +} PACKEDATTR map_point_t; + +typedef struct map_line_s +{ + map_point_t point[2]; +} PACKEDATTR map_line_t; + +extern array_t map_lines; + +#define MAPBITS 12 +#define FRACTOMAPBITS (FRACBITS-MAPBITS) + +// Used by ST StatusBar stuff. +#define AM_MSGHEADER (('a'<<24)+('m'<<16)) +#define AM_MSGENTERED (AM_MSGHEADER | ('e'<<8)) +#define AM_MSGEXITED (AM_MSGHEADER | ('x'<<8)) + +// Called by main loop. +dboolean AM_Responder (event_t* ev); + +// Called by main loop. +void AM_Ticker (void); + +// Called by main loop, +// called instead of view drawer if automap active. +void AM_Drawer (void); + +// Called to force the automap to quit +// if the level is completed while it is up. +void AM_Stop (void); + +// killough 2/22/98: for saving automap information in savegame: + +void AM_Start(void); + +//jff 4/16/98 make externally available + +void AM_clearMarks(void); + +void AM_setMarkParams(int num); + +void AM_SetResolution(void); + +typedef struct +{ + fixed_t x,y; + float fx,fy; +} mpoint_t; + +typedef struct +{ + fixed_t x, y; + fixed_t w, h; + + char label[16]; + int widths[16]; +} markpoint_t; + +extern markpoint_t *markpoints; +extern int markpointnum, markpointnum_max; + +// end changes -- killough 2/22/98 + +// killough 5/2/98: moved from m_misc.c + +//jff 1/7/98 automap colors added +extern int mapcolor_back; // map background +extern int mapcolor_grid; // grid lines color +extern int mapcolor_wall; // normal 1s wall color +extern int mapcolor_fchg; // line at floor height change color +extern int mapcolor_cchg; // line at ceiling height change color +extern int mapcolor_clsd; // line at sector with floor=ceiling color +extern int mapcolor_rkey; // red key color +extern int mapcolor_bkey; // blue key color +extern int mapcolor_ykey; // yellow key color +extern int mapcolor_rdor; // red door color (diff from keys to allow option) +extern int mapcolor_bdor; // blue door color (of enabling one not other) +extern int mapcolor_ydor; // yellow door color +extern int mapcolor_tele; // teleporter line color +extern int mapcolor_secr; // secret sector boundary color +//jff 4/23/98 +extern int mapcolor_exit; // exit line +extern int mapcolor_unsn; // computer map unseen line color +extern int mapcolor_flat; // line with no floor/ceiling changes +extern int mapcolor_sprt; // general sprite color +extern int mapcolor_item; // item sprite color +extern int mapcolor_enemy; // enemy sprite color +extern int mapcolor_frnd; // friendly sprite color +extern int mapcolor_hair; // crosshair color +extern int mapcolor_sngl; // single player arrow color +extern int mapcolor_plyr[4]; // colors for players in multiplayer +extern int mapcolor_me; // consoleplayer's chosen colour +//jff 3/9/98 +extern int map_secret_after; // secrets do not appear til after bagged + +extern int map_always_updates; +extern int map_grid_size; +extern int map_scroll_speed; +extern int map_wheel_zoom; +extern int map_use_multisamling; + +extern int map_textured; +extern int map_textured_trans; +extern int map_textured_overlay_trans; +extern int map_lines_overlay_trans; +extern int map_overlay_pos_x; +extern int map_overlay_pos_y; +extern int map_overlay_pos_width; +extern int map_overlay_pos_height; +extern int map_type; +void M_ChangeMapGridSize(void); +void M_ChangeMapTextured(void); +void M_ChangeMapMultisamling(void); + +typedef struct am_frame_s +{ + fixed_t centerx, centery; + fixed_t sin, cos; + + float centerx_f, centery_f; + float sin_f, cos_f; + + fixed_t bbox[4]; + + int precise; +} am_frame_t; +extern am_frame_t am_frame; + +typedef enum +{ + map_things_appearance_classic, + map_things_appearance_scaled, +#if defined(HAVE_LIBSDL2_IMAGE) && defined(GL_DOOM) + map_things_appearance_icon, +#endif + + map_things_appearance_max +} map_things_appearance_t; +extern map_things_appearance_t map_things_appearance; +extern const char *map_things_appearance_list[]; + +#endif diff --git a/src/d_client.c b/src/d_client.c new file mode 100644 index 0000000..4abd9fc --- /dev/null +++ b/src/d_client.c @@ -0,0 +1,549 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Network client. Passes information to/from server, staying + * synchronised. + * Contains the main wait loop, waiting for network input or + * time before doing the next tic. + * Rewritten for LxDoom, but based around bits of the old code. + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#ifdef USE_SDL_NET + #include "SDL.h" +#endif + +#include "doomtype.h" +#include "doomstat.h" +#include "d_net.h" +#include "z_zone.h" + +#include "d_main.h" +#include "g_game.h" +#include "m_menu.h" +#include "p_checksum.h" + +#include "protocol.h" +#include "i_network.h" +#include "i_system.h" +#include "i_main.h" +#include "i_video.h" +#include "m_argv.h" +#include "r_fps.h" +#include "lprintf.h" +#include "e6y.h" + +#include "m_io.h" + +static dboolean server; +static int remotetic; // Tic expected from the remote +static int remotesend; // Tic expected by the remote +ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; +static ticcmd_t* localcmds; +static unsigned numqueuedpackets; +static packet_header_t** queuedpacket; +int maketic; +int ticdup = 1; +static int xtratics = 0; +int wanted_player_number; +int solo_net = 0; + +static void D_QuitNetGame (void); + +#ifndef HAVE_NET +doomcom_t* doomcom; +#endif + +#ifdef HAVE_NET +void D_InitNetGame (void) +{ + int i; + int numplayers = 1; + + i = M_CheckParm("-net"); + if (i && i < myargc-1) i++; + + if (!(netgame = server = !!i)) { + playeringame[consoleplayer = 0] = true; + // e6y + // for play, recording or playback using "single-player coop" mode. + // Equivalent to using prboom_server with -N 1 + solo_net = (M_CheckParm("-solo-net") != 0); + coop_spawns = (M_CheckParm("-coop_spawns") != 0); + netgame = solo_net; + } else { + // Get game info from server + packet_header_t *packet = Z_Malloc(1000, PU_STATIC, NULL); + struct setup_packet_s *sinfo = (void*)(packet+1); + struct { packet_header_t head; short pn; } PACKEDATTR initpacket; + + I_InitNetwork(); + udp_socket = I_Socket(0); + I_ConnectToServer(myargv[i]); + + do + { + do { + // Send init packet + initpacket.pn = doom_htons(wanted_player_number); + packet_set(&initpacket.head, PKT_INIT, 0); + I_SendPacket(&initpacket.head, sizeof(initpacket)); + I_WaitForPacket(5000); + } while (!I_GetPacket(packet, 1000)); + if (packet->type == PKT_DOWN) I_Error("Server aborted the game"); + } while (packet->type != PKT_SETUP); + + // Once we have been accepted by the server, we should tell it when we leave + I_AtExit(D_QuitNetGame, true); + + // Get info from the setup packet + consoleplayer = sinfo->yourplayer; + compatibility_level = sinfo->complevel; + G_Compatibility(); + startskill = sinfo->skill; + deathmatch = sinfo->deathmatch; + startmap = sinfo->level; + startepisode = sinfo->episode; + ticdup = sinfo->ticdup; + xtratics = sinfo->extratic; + G_ReadOptions(sinfo->game_options); + + lprintf(LO_INFO, "\tjoined game as player %d/%d; %d WADs specified\n", + consoleplayer+1, numplayers = sinfo->players, sinfo->numwads); + { + char *p = sinfo->wadnames; + int i = sinfo->numwads; + + while (i--) { + D_AddFile(p, source_net); + p += strlen(p) + 1; + } + } + Z_Free(packet); + } + localcmds = netcmds[displayplayer = consoleplayer]; + for (i=0; iconsoleplayer = 0; + doomcom->numnodes = 0; doomcom->numplayers = 1; + localcmds = netcmds[consoleplayer]; + solo_net = (M_CheckParm("-solo-net") != 0); + coop_spawns = (M_CheckParm("-coop_spawns") != 0); + netgame = solo_net; + + for (i=0; inumplayers; i++) + playeringame[i] = true; + for (; iconsoleplayer; +} +#endif // HAVE_NET + +#ifdef HAVE_NET +void D_CheckNetGame(void) +{ + packet_header_t *packet = Z_Malloc(sizeof(packet_header_t)+1, PU_STATIC, NULL); + + if (server) { + lprintf(LO_INFO, "D_CheckNetGame: waiting for server to signal game start\n"); + do { + while (!I_GetPacket(packet, sizeof(packet_header_t)+1)) { + packet_set(packet, PKT_GO, 0); + *(byte*)(packet+1) = consoleplayer; + I_SendPacket(packet, sizeof(packet_header_t)+1); + I_uSleep(100000); + } + } while (packet->type != PKT_GO); + } + Z_Free(packet); +} + +dboolean D_NetGetWad(const char* name) +{ +#if defined(HAVE_WAIT_H) + size_t psize = sizeof(packet_header_t) + strlen(name) + 500; + packet_header_t *packet; + dboolean done = false; + + if (!server || strchr(name, '/')) return false; // If it contains path info, reject + + do { + // Send WAD request to remote + packet = Z_Malloc(psize, PU_STATIC, NULL); + packet_set(packet, PKT_WAD, 0); + *(byte*)(packet+1) = consoleplayer; + strcpy(1+(byte*)(packet+1), name); + I_SendPacket(packet, sizeof(packet_header_t) + strlen(name) + 2); + + I_uSleep(10000); + } while (!I_GetPacket(packet, psize) || (packet->type != PKT_WAD)); + Z_Free(packet); + + if (!strcasecmp((void*)(packet+1), name)) { + pid_t pid; + int rv; + byte *p = (byte*)(packet+1) + strlen(name) + 1; + + /* Automatic wad file retrieval using wget (supports http and ftp, using URLs) + * Unix systems have all these commands handy, this kind of thing is easy + * Any windo$e port will have some awkward work replacing these. + */ + /* cph - caution here. This is data from an untrusted source. + * Don't pass it via a shell. */ + if ((pid = fork()) == -1) + perror("fork"); + else if (!pid) { + /* Child chains to wget, does the download */ + execlp("wget", "wget", p, NULL); + } + /* This is the parent, i.e. main LxDoom process */ + wait(&rv); + if (!(done = !M_access(name, R_OK))) { + if (!strcmp(p+strlen(p)-4, ".zip")) { + p = strrchr(p, '/')+1; + if ((pid = fork()) == -1) + perror("fork"); + else if (!pid) { + /* Child executes decompressor */ + execlp("unzip", "unzip", p, name, NULL); + } + /* Parent waits for the file */ + wait(&rv); + done = !!M_access(name, R_OK); + } + /* Add more decompression protocols here as desired */ + } + Z_Free(buffer); + } + return done; +#else /* HAVE_WAIT_H */ + return false; +#endif +} + +void NetUpdate(void) +{ + static int lastmadetic; + if (isExtraDDisplay) + return; + if (server) { // Receive network packets + size_t recvlen; + packet_header_t *packet = Z_Malloc(10000, PU_STATIC, NULL); + while ((recvlen = I_GetPacket(packet, 10000))) { + switch(packet->type) { + case PKT_TICS: + { + byte *p = (void*)(packet+1); + int tics = *p++; + unsigned long ptic = doom_ntohl(packet->tic); + if (ptic > (unsigned)remotetic) { // Missed some + packet_set(packet, PKT_RETRANS, remotetic); + *(byte*)(packet+1) = consoleplayer; + I_SendPacket(packet, sizeof(*packet)+1); + } else { + if (ptic + tics <= (unsigned)remotetic) break; // Will not improve things + remotetic = ptic; + while (tics--) { + int players = *p++; + while (players--) { + int n = *p++; + RawToTic(&netcmds[n][remotetic%BACKUPTICS], p); + p += sizeof(ticcmd_t); + } + remotetic++; + } + } + } + break; + case PKT_RETRANS: // Resend request + remotesend = doom_ntohl(packet->tic); + break; + case PKT_DOWN: // Server downed + { + int j; + for (j=0; j 0 ? newtics : 0); + lastmadetic += newtics; + if (ffmap) newtics++; + while (newtics--) { + I_StartTic(); + if (maketic - gametic > BACKUPTICS/2) break; + + // e6y + // Eliminating the sudden jump of six frames(BACKUPTICS/2) + // after change of realtic_clock_rate. + if (maketic - gametic && gametic <= force_singletics_to && realtic_clock_rate < 200) break; + + G_BuildTiccmd(&localcmds[maketic%BACKUPTICS]); + maketic++; + } + if (server && maketic > remotesend) { // Send the tics to the server + int sendtics; + remotesend -= xtratics; + if (remotesend < 0) remotesend = 0; + sendtics = MIN(maketic - remotesend, 128); // limit number of sent tics (CVE-2019-20797) + { + size_t pkt_size = sizeof(packet_header_t) + 2 + sendtics * sizeof(ticcmd_t); + packet_header_t *packet = Z_Malloc(pkt_size, PU_STATIC, NULL); + + packet_set(packet, PKT_TICC, maketic - sendtics); + *(byte*)(packet+1) = sendtics; + *(((byte*)(packet+1))+1) = consoleplayer; + { + void *tic = ((byte*)(packet+1)) +2; + while (sendtics--) { + TicToRaw(tic, &localcmds[remotesend++%BACKUPTICS]); + tic = (byte *)tic + sizeof(ticcmd_t); + } + } + I_SendPacket(packet, pkt_size); + Z_Free(packet); + } + } + } +} +#else + +void D_BuildNewTiccmds(void) +{ + static int lastmadetic; + int newtics = I_GetTime() - lastmadetic; + lastmadetic += newtics; + while (newtics--) + { + I_StartTic(); + if (maketic - gametic > BACKUPTICS/2) break; + G_BuildTiccmd(&localcmds[maketic%BACKUPTICS]); + maketic++; + } +} +#endif + +#ifdef HAVE_NET +/* cph - data passed to this must be in the Doom (little-) endian */ +void D_NetSendMisc(netmisctype_t type, size_t len, void* data) +{ + if (server) { + size_t size = sizeof(packet_header_t) + 3*sizeof(int) + len; + packet_header_t *packet = Z_Malloc(size, PU_STATIC, NULL); + int *p = (void*)(packet+1); + + packet_set(packet, PKT_EXTRA, gametic); + *p++ = LittleLong(type); *p++ = LittleLong(consoleplayer); *p++ = LittleLong(len); + memcpy(p, data, len); + I_SendPacket(packet, size); + + Z_Free(packet); + } +} + +static void CheckQueuedPackets(void) +{ + int i; + for (i=0; (unsigned)itic) <= gametic) + switch (queuedpacket[i]->type) { + case PKT_QUIT: // Player quit the game + { + int pn = *(byte*)(queuedpacket[i]+1); + playeringame[pn] = false; + doom_printf("Player %d left the game\n", pn); + } + break; + case PKT_EXTRA: + { + int *p = (int*)(queuedpacket[i]+1); + size_t len = LittleLong(*(p+2)); + switch (LittleLong(*p)) { + case nm_plcolour: + G_ChangedPlayerColour(LittleLong(*(p+1)), LittleLong(*(p+3))); + break; + case nm_savegamename: + if (len < SAVEDESCLEN) { + memcpy(savedescription, p+3, len); + // Force terminating 0 in case + savedescription[len] = 0; + } + break; + } + } + break; + default: // Should not be queued + break; + } + + { // Requeue remaining packets + int newnum = 0; + packet_header_t **newqueue = NULL; + + for (i=0; (unsigned)itic) > gametic) { + newqueue = Z_Realloc(newqueue, ++newnum * sizeof *newqueue, + PU_STATIC, NULL); + newqueue[newnum-1] = queuedpacket[i]; + } else Z_Free(queuedpacket[i]); + + Z_Free(queuedpacket); + numqueuedpackets = newnum; queuedpacket = newqueue; + } +} +#endif // HAVE_NET + +void TryRunTics (void) +{ + int runtics; + int entertime = I_GetTime(); + + // Wait for tics to run + while (1) { +#ifdef HAVE_NET + NetUpdate(); +#else + D_BuildNewTiccmds(); +#endif + runtics = (server ? remotetic : maketic) - gametic; + if (!runtics) { + if (!movement_smooth || !window_focused) { +#ifdef HAVE_NET + if (server) + I_WaitForPacket(ms_to_next_tick); + else +#endif + I_uSleep(ms_to_next_tick*1000); + } + if (I_GetTime() - entertime > 10) { +#ifdef HAVE_NET + if (server) { + char buf[sizeof(packet_header_t)+1]; + remotesend--; + packet_set((packet_header_t *)buf, PKT_RETRANS, remotetic); + buf[sizeof(buf)-1] = consoleplayer; + I_SendPacket((packet_header_t *)buf, sizeof buf); + } +#endif + M_Ticker(); return; + } + //if ((displaytime) < (tic_vars.next-SDL_GetTicks())) + if (gametic > 0) + { + WasRenderedInTryRunTics = true; + if (movement_smooth && gamestate==wipegamestate) + { + isExtraDDisplay = true; + D_Display(I_GetTimeFrac()); + isExtraDDisplay = false; + } + } + } else break; + } + + while (runtics--) { +#ifdef HAVE_NET + if (server) CheckQueuedPackets(); +#endif + if (advancedemo) + D_DoAdvanceDemo (); + M_Ticker (); + G_Ticker (); + P_Checksum(gametic); + gametic++; +#ifdef HAVE_NET + NetUpdate(); // Keep sending our tics to avoid stalling remote nodes +#endif + } +} + +#ifdef HAVE_NET +static void D_QuitNetGame (void) +{ + byte buf[1 + sizeof(packet_header_t)]; + packet_header_t *packet = (void*)buf; + int i; + + if (!server) return; + buf[sizeof(packet_header_t)] = consoleplayer; + packet_set(packet, PKT_QUIT, gametic); + + for (i=0; i<4; i++) { + I_SendPacket(packet, 1 + sizeof(packet_header_t)); + I_uSleep(10000); + } +} +#endif diff --git a/src/d_deh.c b/src/d_deh.c new file mode 100644 index 0000000..e5a2f7d --- /dev/null +++ b/src/d_deh.c @@ -0,0 +1,3366 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Dehacked file support + * New for the TeamTNT "Boom" engine + * + * Author: Ty Halderman, TeamTNT + * + *--------------------------------------------------------------------*/ + +// killough 5/2/98: fixed headers, removed rendunant external declarations: +#include "doomdef.h" +#include "doomtype.h" +#include "doomstat.h" +#include "d_deh.h" +#include "sounds.h" +#include "info.h" +#include "m_cheat.h" +#include "p_inter.h" +#include "p_enemy.h" +#include "g_game.h" +#include "d_think.h" +#include "w_wad.h" +#include "m_argv.h" +#include "m_misc.h" +#include "e6y.h"//e6y + +// CPhipps - modify to use logical output routine +#include "lprintf.h" + +#include "m_io.h" + +#define TRUE 1 +#define FALSE 0 + +// e6y: for compatibility with BOOM deh parser +int deh_strcasecmp(const char *str1, const char *str2) +{ + if (prboom_comp[PC_BOOM_DEH_PARSER].state && + compatibility_level >= boom_compatibility_compatibility && + compatibility_level <= boom_202_compatibility) + { + return strcmp(str1, str2); + } + else + { + return strcasecmp(str1, str2); + } +} +const char * deh_getBitsDelims(void) +{ + if (prboom_comp[PC_BOOM_DEH_PARSER].state && + compatibility_level >= boom_compatibility_compatibility && + compatibility_level <= boom_202_compatibility) + { + return "+"; + } + else + { + return ",+| \t\f\r"; + } +} + +// If false, dehacked cheat replacements are ignored. +int deh_apply_cheats = true; + +// killough 10/98: new functions, to allow processing DEH files in-memory +// (e.g. from wads) + +typedef struct { + /* cph 2006/08/06 - + * if lump != NULL, lump is the start of the lump, + * inp is the current read pos. */ + const byte *inp, *lump; + long size; + /* else, !lump, and f is the file being read */ + FILE* f; +} DEHFILE; + +// killough 10/98: emulate IO whether input really comes from a file or not + +static char *dehfgets(char *buf, size_t n, DEHFILE *fp) +{ + if (!fp->lump) // If this is a real file, + return (fgets)(buf, n, fp->f); // return regular fgets + if (!n || !*fp->inp || fp->size<=0) // If no more characters + return NULL; + if (n==1) + fp->size--, *buf = *fp->inp++; + else + { // copy buffer + char *p = buf; + while (n>1 && *fp->inp && fp->size && + (n--, fp->size--, *p++ = *fp->inp++) != '\n') + ; + *p = 0; + } + return buf; // Return buffer pointer +} + +static int dehfeof(DEHFILE *fp) +{ + return !fp->lump ? feof(fp->f) : !*fp->inp || fp->size<=0; +} + +static int dehfgetc(DEHFILE *fp) +{ + return !fp->lump ? fgetc(fp->f) : fp->size > 0 ? + fp->size--, *fp->inp++ : EOF; +} + +static long dehftell(DEHFILE *fp) +{ + return !fp->lump ? ftell(fp->f) : (fp->inp - fp->lump); +} + +static int dehfseek(DEHFILE *fp, long offset) +{ + if (!fp->lump) + return fseek(fp->f, offset, SEEK_SET); + else + { + long total = (fp->inp - fp->lump) + fp->size; + offset = BETWEEN(0, total, offset); + fp->inp = fp->lump + offset; + fp->size = total - offset; + return 0; + } +} + +// haleyjd 9/22/99 +int HelperThing = -1; // in P_SpawnMapThing to substitute helper thing + +// variables used in other routines +dboolean deh_pars = FALSE; // in wi_stuff to allow pars in modified games + +// #include "d_deh.h" -- we don't do that here but we declare the +// variables. This externalizes everything that there is a string +// set for in the language files. See d_deh.h for detailed comments, +// original English values etc. These are set to the macro values, +// which are set by D_ENGLSH.H or D_FRENCH.H(etc). BEX files are a +// better way of changing these strings globally by language. + +// ==================================================================== +// Any of these can be changed using the bex extensions +#include "dstrings.h" // to get the initial values +/* cph - const's + * - removed redundant "can't XXX in a netgame" strings. + */ +const char *s_D_DEVSTR = D_DEVSTR; +const char *s_D_CDROM = D_CDROM; +const char *s_PRESSKEY = PRESSKEY; +const char *s_PRESSYN = PRESSYN; +const char *s_QUITMSG = QUITMSG; +const char *s_QSAVESPOT = QSAVESPOT; // PRESSKEY; +const char *s_SAVEDEAD = SAVEDEAD; // PRESSKEY; // remove duplicate y/n +const char *s_QSPROMPT = QSPROMPT; // PRESSYN; +const char *s_QLPROMPT = QLPROMPT; // PRESSYN; +const char *s_NEWGAME = NEWGAME; // PRESSKEY; +const char *s_RESTARTLEVEL= RESTARTLEVEL; // PRESSYN; +const char *s_NIGHTMARE = NIGHTMARE; // PRESSYN; +const char *s_SWSTRING = SWSTRING; // PRESSKEY; +const char *s_MSGOFF = MSGOFF; +const char *s_MSGON = MSGON; +const char *s_NETEND = NETEND; // PRESSKEY; +const char *s_ENDGAME = ENDGAME; // PRESSYN; // killough 4/4/98: end +const char *s_DOSY = DOSY; +const char *s_DETAILHI = DETAILHI; +const char *s_DETAILLO = DETAILLO; +const char *s_GAMMALVL0 = GAMMALVL0; +const char *s_GAMMALVL1 = GAMMALVL1; +const char *s_GAMMALVL2 = GAMMALVL2; +const char *s_GAMMALVL3 = GAMMALVL3; +const char *s_GAMMALVL4 = GAMMALVL4; +const char *s_EMPTYSTRING = EMPTYSTRING; +const char *s_GOTARMOR = GOTARMOR; +const char *s_GOTMEGA = GOTMEGA; +const char *s_GOTHTHBONUS = GOTHTHBONUS; +const char *s_GOTARMBONUS = GOTARMBONUS; +const char *s_GOTSTIM = GOTSTIM; +const char *s_GOTMEDINEED = GOTMEDINEED; +const char *s_GOTMEDIKIT = GOTMEDIKIT; +const char *s_GOTSUPER = GOTSUPER; +const char *s_GOTBLUECARD = GOTBLUECARD; +const char *s_GOTYELWCARD = GOTYELWCARD; +const char *s_GOTREDCARD = GOTREDCARD; +const char *s_GOTBLUESKUL = GOTBLUESKUL; +const char *s_GOTYELWSKUL = GOTYELWSKUL; +const char *s_GOTREDSKULL = GOTREDSKULL; +const char *s_GOTINVUL = GOTINVUL; +const char *s_GOTBERSERK = GOTBERSERK; +const char *s_GOTINVIS = GOTINVIS; +const char *s_GOTSUIT = GOTSUIT; +const char *s_GOTMAP = GOTMAP; +const char *s_GOTVISOR = GOTVISOR; +const char *s_GOTMSPHERE = GOTMSPHERE; +const char *s_GOTCLIP = GOTCLIP; +const char *s_GOTCLIPBOX = GOTCLIPBOX; +const char *s_GOTROCKET = GOTROCKET; +const char *s_GOTROCKBOX = GOTROCKBOX; +const char *s_GOTCELL = GOTCELL; +const char *s_GOTCELLBOX = GOTCELLBOX; +const char *s_GOTSHELLS = GOTSHELLS; +const char *s_GOTSHELLBOX = GOTSHELLBOX; +const char *s_GOTBACKPACK = GOTBACKPACK; +const char *s_GOTBFG9000 = GOTBFG9000; +const char *s_GOTCHAINGUN = GOTCHAINGUN; +const char *s_GOTCHAINSAW = GOTCHAINSAW; +const char *s_GOTLAUNCHER = GOTLAUNCHER; +const char *s_GOTPLASMA = GOTPLASMA; +const char *s_GOTSHOTGUN = GOTSHOTGUN; +const char *s_GOTSHOTGUN2 = GOTSHOTGUN2; +const char *s_PD_BLUEO = PD_BLUEO; +const char *s_PD_REDO = PD_REDO; +const char *s_PD_YELLOWO = PD_YELLOWO; +const char *s_PD_BLUEK = PD_BLUEK; +const char *s_PD_REDK = PD_REDK; +const char *s_PD_YELLOWK = PD_YELLOWK; +const char *s_PD_BLUEC = PD_BLUEC; +const char *s_PD_REDC = PD_REDC; +const char *s_PD_YELLOWC = PD_YELLOWC; +const char *s_PD_BLUES = PD_BLUES; +const char *s_PD_REDS = PD_REDS; +const char *s_PD_YELLOWS = PD_YELLOWS; +const char *s_PD_ANY = PD_ANY; +const char *s_PD_ALL3 = PD_ALL3; +const char *s_PD_ALL6 = PD_ALL6; +const char *s_GGSAVED = GGSAVED; +const char *s_HUSTR_MSGU = HUSTR_MSGU; +const char *s_HUSTR_E1M1 = HUSTR_E1M1; +const char *s_HUSTR_E1M2 = HUSTR_E1M2; +const char *s_HUSTR_E1M3 = HUSTR_E1M3; +const char *s_HUSTR_E1M4 = HUSTR_E1M4; +const char *s_HUSTR_E1M5 = HUSTR_E1M5; +const char *s_HUSTR_E1M6 = HUSTR_E1M6; +const char *s_HUSTR_E1M7 = HUSTR_E1M7; +const char *s_HUSTR_E1M8 = HUSTR_E1M8; +const char *s_HUSTR_E1M9 = HUSTR_E1M9; +const char *s_HUSTR_E2M1 = HUSTR_E2M1; +const char *s_HUSTR_E2M2 = HUSTR_E2M2; +const char *s_HUSTR_E2M3 = HUSTR_E2M3; +const char *s_HUSTR_E2M4 = HUSTR_E2M4; +const char *s_HUSTR_E2M5 = HUSTR_E2M5; +const char *s_HUSTR_E2M6 = HUSTR_E2M6; +const char *s_HUSTR_E2M7 = HUSTR_E2M7; +const char *s_HUSTR_E2M8 = HUSTR_E2M8; +const char *s_HUSTR_E2M9 = HUSTR_E2M9; +const char *s_HUSTR_E3M1 = HUSTR_E3M1; +const char *s_HUSTR_E3M2 = HUSTR_E3M2; +const char *s_HUSTR_E3M3 = HUSTR_E3M3; +const char *s_HUSTR_E3M4 = HUSTR_E3M4; +const char *s_HUSTR_E3M5 = HUSTR_E3M5; +const char *s_HUSTR_E3M6 = HUSTR_E3M6; +const char *s_HUSTR_E3M7 = HUSTR_E3M7; +const char *s_HUSTR_E3M8 = HUSTR_E3M8; +const char *s_HUSTR_E3M9 = HUSTR_E3M9; +const char *s_HUSTR_E4M1 = HUSTR_E4M1; +const char *s_HUSTR_E4M2 = HUSTR_E4M2; +const char *s_HUSTR_E4M3 = HUSTR_E4M3; +const char *s_HUSTR_E4M4 = HUSTR_E4M4; +const char *s_HUSTR_E4M5 = HUSTR_E4M5; +const char *s_HUSTR_E4M6 = HUSTR_E4M6; +const char *s_HUSTR_E4M7 = HUSTR_E4M7; +const char *s_HUSTR_E4M8 = HUSTR_E4M8; +const char *s_HUSTR_E4M9 = HUSTR_E4M9; +const char *s_HUSTR_1 = HUSTR_1; +const char *s_HUSTR_2 = HUSTR_2; +const char *s_HUSTR_3 = HUSTR_3; +const char *s_HUSTR_4 = HUSTR_4; +const char *s_HUSTR_5 = HUSTR_5; +const char *s_HUSTR_6 = HUSTR_6; +const char *s_HUSTR_7 = HUSTR_7; +const char *s_HUSTR_8 = HUSTR_8; +const char *s_HUSTR_9 = HUSTR_9; +const char *s_HUSTR_10 = HUSTR_10; +const char *s_HUSTR_11 = HUSTR_11; +const char *s_HUSTR_12 = HUSTR_12; +const char *s_HUSTR_13 = HUSTR_13; +const char *s_HUSTR_14 = HUSTR_14; +const char *s_HUSTR_15 = HUSTR_15; +const char *s_HUSTR_16 = HUSTR_16; +const char *s_HUSTR_17 = HUSTR_17; +const char *s_HUSTR_18 = HUSTR_18; +const char *s_HUSTR_19 = HUSTR_19; +const char *s_HUSTR_20 = HUSTR_20; +const char *s_HUSTR_21 = HUSTR_21; +const char *s_HUSTR_22 = HUSTR_22; +const char *s_HUSTR_23 = HUSTR_23; +const char *s_HUSTR_24 = HUSTR_24; +const char *s_HUSTR_25 = HUSTR_25; +const char *s_HUSTR_26 = HUSTR_26; +const char *s_HUSTR_27 = HUSTR_27; +const char *s_HUSTR_28 = HUSTR_28; +const char *s_HUSTR_29 = HUSTR_29; +const char *s_HUSTR_30 = HUSTR_30; +const char *s_HUSTR_31 = HUSTR_31; +const char *s_HUSTR_32 = HUSTR_32; +const char *s_HUSTR_33 = HUSTR_33; +const char *s_PHUSTR_1 = PHUSTR_1; +const char *s_PHUSTR_2 = PHUSTR_2; +const char *s_PHUSTR_3 = PHUSTR_3; +const char *s_PHUSTR_4 = PHUSTR_4; +const char *s_PHUSTR_5 = PHUSTR_5; +const char *s_PHUSTR_6 = PHUSTR_6; +const char *s_PHUSTR_7 = PHUSTR_7; +const char *s_PHUSTR_8 = PHUSTR_8; +const char *s_PHUSTR_9 = PHUSTR_9; +const char *s_PHUSTR_10 = PHUSTR_10; +const char *s_PHUSTR_11 = PHUSTR_11; +const char *s_PHUSTR_12 = PHUSTR_12; +const char *s_PHUSTR_13 = PHUSTR_13; +const char *s_PHUSTR_14 = PHUSTR_14; +const char *s_PHUSTR_15 = PHUSTR_15; +const char *s_PHUSTR_16 = PHUSTR_16; +const char *s_PHUSTR_17 = PHUSTR_17; +const char *s_PHUSTR_18 = PHUSTR_18; +const char *s_PHUSTR_19 = PHUSTR_19; +const char *s_PHUSTR_20 = PHUSTR_20; +const char *s_PHUSTR_21 = PHUSTR_21; +const char *s_PHUSTR_22 = PHUSTR_22; +const char *s_PHUSTR_23 = PHUSTR_23; +const char *s_PHUSTR_24 = PHUSTR_24; +const char *s_PHUSTR_25 = PHUSTR_25; +const char *s_PHUSTR_26 = PHUSTR_26; +const char *s_PHUSTR_27 = PHUSTR_27; +const char *s_PHUSTR_28 = PHUSTR_28; +const char *s_PHUSTR_29 = PHUSTR_29; +const char *s_PHUSTR_30 = PHUSTR_30; +const char *s_PHUSTR_31 = PHUSTR_31; +const char *s_PHUSTR_32 = PHUSTR_32; +const char *s_THUSTR_1 = THUSTR_1; +const char *s_THUSTR_2 = THUSTR_2; +const char *s_THUSTR_3 = THUSTR_3; +const char *s_THUSTR_4 = THUSTR_4; +const char *s_THUSTR_5 = THUSTR_5; +const char *s_THUSTR_6 = THUSTR_6; +const char *s_THUSTR_7 = THUSTR_7; +const char *s_THUSTR_8 = THUSTR_8; +const char *s_THUSTR_9 = THUSTR_9; +const char *s_THUSTR_10 = THUSTR_10; +const char *s_THUSTR_11 = THUSTR_11; +const char *s_THUSTR_12 = THUSTR_12; +const char *s_THUSTR_13 = THUSTR_13; +const char *s_THUSTR_14 = THUSTR_14; +const char *s_THUSTR_15 = THUSTR_15; +const char *s_THUSTR_16 = THUSTR_16; +const char *s_THUSTR_17 = THUSTR_17; +const char *s_THUSTR_18 = THUSTR_18; +const char *s_THUSTR_19 = THUSTR_19; +const char *s_THUSTR_20 = THUSTR_20; +const char *s_THUSTR_21 = THUSTR_21; +const char *s_THUSTR_22 = THUSTR_22; +const char *s_THUSTR_23 = THUSTR_23; +const char *s_THUSTR_24 = THUSTR_24; +const char *s_THUSTR_25 = THUSTR_25; +const char *s_THUSTR_26 = THUSTR_26; +const char *s_THUSTR_27 = THUSTR_27; +const char *s_THUSTR_28 = THUSTR_28; +const char *s_THUSTR_29 = THUSTR_29; +const char *s_THUSTR_30 = THUSTR_30; +const char *s_THUSTR_31 = THUSTR_31; +const char *s_THUSTR_32 = THUSTR_32; +const char *s_HUSTR_CHATMACRO1 = HUSTR_CHATMACRO1; +const char *s_HUSTR_CHATMACRO2 = HUSTR_CHATMACRO2; +const char *s_HUSTR_CHATMACRO3 = HUSTR_CHATMACRO3; +const char *s_HUSTR_CHATMACRO4 = HUSTR_CHATMACRO4; +const char *s_HUSTR_CHATMACRO5 = HUSTR_CHATMACRO5; +const char *s_HUSTR_CHATMACRO6 = HUSTR_CHATMACRO6; +const char *s_HUSTR_CHATMACRO7 = HUSTR_CHATMACRO7; +const char *s_HUSTR_CHATMACRO8 = HUSTR_CHATMACRO8; +const char *s_HUSTR_CHATMACRO9 = HUSTR_CHATMACRO9; +const char *s_HUSTR_CHATMACRO0 = HUSTR_CHATMACRO0; +const char *s_HUSTR_TALKTOSELF1 = HUSTR_TALKTOSELF1; +const char *s_HUSTR_TALKTOSELF2 = HUSTR_TALKTOSELF2; +const char *s_HUSTR_TALKTOSELF3 = HUSTR_TALKTOSELF3; +const char *s_HUSTR_TALKTOSELF4 = HUSTR_TALKTOSELF4; +const char *s_HUSTR_TALKTOSELF5 = HUSTR_TALKTOSELF5; +const char *s_HUSTR_MESSAGESENT = HUSTR_MESSAGESENT; +const char *s_HUSTR_PLRGREEN = HUSTR_PLRGREEN; +const char *s_HUSTR_PLRINDIGO = HUSTR_PLRINDIGO; +const char *s_HUSTR_PLRBROWN = HUSTR_PLRBROWN; +const char *s_HUSTR_PLRRED = HUSTR_PLRRED; +const char *s_AMSTR_FOLLOWON = AMSTR_FOLLOWON; +const char *s_AMSTR_FOLLOWOFF = AMSTR_FOLLOWOFF; +const char *s_AMSTR_GRIDON = AMSTR_GRIDON; +const char *s_AMSTR_GRIDOFF = AMSTR_GRIDOFF; +const char *s_AMSTR_MARKEDSPOT = AMSTR_MARKEDSPOT; +const char *s_AMSTR_MARKSCLEARED = AMSTR_MARKSCLEARED; +// CPhipps - automap rotate & overlay +const char* s_AMSTR_ROTATEON = AMSTR_ROTATEON; +const char* s_AMSTR_ROTATEOFF = AMSTR_ROTATEOFF; +const char* s_AMSTR_OVERLAYON = AMSTR_OVERLAYON; +const char* s_AMSTR_OVERLAYOFF = AMSTR_OVERLAYOFF; +// e6y: textured automap +const char* s_AMSTR_TEXTUREDON = AMSTR_TEXTUREDON; +const char* s_AMSTR_TEXTUREDOFF = AMSTR_TEXTUREDOFF; + +const char *s_STSTR_MUS = STSTR_MUS; +const char *s_STSTR_NOMUS = STSTR_NOMUS; +const char *s_STSTR_DQDON = STSTR_DQDON; +const char *s_STSTR_DQDOFF = STSTR_DQDOFF; +const char *s_STSTR_KFAADDED = STSTR_KFAADDED; +const char *s_STSTR_FAADDED = STSTR_FAADDED; +const char *s_STSTR_NCON = STSTR_NCON; +const char *s_STSTR_NCOFF = STSTR_NCOFF; +const char *s_STSTR_BEHOLD = STSTR_BEHOLD; +const char *s_STSTR_BEHOLDX = STSTR_BEHOLDX; +const char *s_STSTR_CHOPPERS = STSTR_CHOPPERS; +const char *s_STSTR_CLEV = STSTR_CLEV; +const char *s_STSTR_COMPON = STSTR_COMPON; +const char *s_STSTR_COMPOFF = STSTR_COMPOFF; +const char *s_E1TEXT = E1TEXT; +const char *s_E2TEXT = E2TEXT; +const char *s_E3TEXT = E3TEXT; +const char *s_E4TEXT = E4TEXT; +const char *s_C1TEXT = C1TEXT; +const char *s_C2TEXT = C2TEXT; +const char *s_C3TEXT = C3TEXT; +const char *s_C4TEXT = C4TEXT; +const char *s_C5TEXT = C5TEXT; +const char *s_C6TEXT = C6TEXT; +const char *s_P1TEXT = P1TEXT; +const char *s_P2TEXT = P2TEXT; +const char *s_P3TEXT = P3TEXT; +const char *s_P4TEXT = P4TEXT; +const char *s_P5TEXT = P5TEXT; +const char *s_P6TEXT = P6TEXT; +const char *s_T1TEXT = T1TEXT; +const char *s_T2TEXT = T2TEXT; +const char *s_T3TEXT = T3TEXT; +const char *s_T4TEXT = T4TEXT; +const char *s_T5TEXT = T5TEXT; +const char *s_T6TEXT = T6TEXT; +const char *s_CC_ZOMBIE = CC_ZOMBIE; +const char *s_CC_SHOTGUN = CC_SHOTGUN; +const char *s_CC_HEAVY = CC_HEAVY; +const char *s_CC_IMP = CC_IMP; +const char *s_CC_DEMON = CC_DEMON; +const char *s_CC_LOST = CC_LOST; +const char *s_CC_CACO = CC_CACO; +const char *s_CC_HELL = CC_HELL; +const char *s_CC_BARON = CC_BARON; +const char *s_CC_ARACH = CC_ARACH; +const char *s_CC_PAIN = CC_PAIN; +const char *s_CC_REVEN = CC_REVEN; +const char *s_CC_MANCU = CC_MANCU; +const char *s_CC_ARCH = CC_ARCH; +const char *s_CC_SPIDER = CC_SPIDER; +const char *s_CC_CYBER = CC_CYBER; +const char *s_CC_HERO = CC_HERO; +// Ty 03/30/98 - new substitutions for background textures +// during int screens +const char *bgflatE1 = "FLOOR4_8"; // end of DOOM Episode 1 +const char *bgflatE2 = "SFLR6_1"; // end of DOOM Episode 2 +const char *bgflatE3 = "MFLR8_4"; // end of DOOM Episode 3 +const char *bgflatE4 = "MFLR8_3"; // end of DOOM Episode 4 +const char *bgflat06 = "SLIME16"; // DOOM2 after MAP06 +const char *bgflat11 = "RROCK14"; // DOOM2 after MAP11 +const char *bgflat20 = "RROCK07"; // DOOM2 after MAP20 +const char *bgflat30 = "RROCK17"; // DOOM2 after MAP30 +const char *bgflat15 = "RROCK13"; // DOOM2 going MAP15 to MAP31 +const char *bgflat31 = "RROCK19"; // DOOM2 going MAP31 to MAP32 +const char *bgcastcall = "BOSSBACK"; // Panel behind cast call + +const char *startup1 = ""; // blank lines are default and are not printed +const char *startup2 = ""; +const char *startup3 = ""; +const char *startup4 = ""; +const char *startup5 = ""; + +/* Ty 05/03/98 - externalized + * cph - updated for prboom */ +const char *savegamename = PACKAGE_TARNAME"-savegame"; + +// end d_deh.h variable declarations +// ==================================================================== + +// Do this for a lookup--the pointer (loaded above) is cross-referenced +// to a string key that is the same as the define above. We will use +// strdups to set these new values that we read from the file, orphaning +// the original value set above. + +// CPhipps - make strings pointed to const +typedef struct { + const char **ppstr; // doubly indirect pointer to string + const char *lookup; // pointer to lookup string name + const char *orig; +} deh_strs; + +/* CPhipps - const + * - removed redundant "Can't XXX in a netgame" strings + */ +static deh_strs deh_strlookup[] = { + {&s_D_DEVSTR,"D_DEVSTR"}, + {&s_D_CDROM,"D_CDROM"}, + {&s_PRESSKEY,"PRESSKEY"}, + {&s_PRESSYN,"PRESSYN"}, + {&s_QUITMSG,"QUITMSG"}, + {&s_QSAVESPOT,"QSAVESPOT"}, + {&s_SAVEDEAD,"SAVEDEAD"}, + /* cph - disabled to prevent format string attacks in WAD files + {&s_QSPROMPT,"QSPROMPT"}, + {&s_QLPROMPT,"QLPROMPT"},*/ + {&s_NEWGAME,"NEWGAME"}, + {&s_RESTARTLEVEL,"RESTARTLEVEL"}, + {&s_NIGHTMARE,"NIGHTMARE"}, + {&s_SWSTRING,"SWSTRING"}, + {&s_MSGOFF,"MSGOFF"}, + {&s_MSGON,"MSGON"}, + {&s_NETEND,"NETEND"}, + {&s_ENDGAME,"ENDGAME"}, + {&s_DOSY,"DOSY"}, + {&s_DETAILHI,"DETAILHI"}, + {&s_DETAILLO,"DETAILLO"}, + {&s_GAMMALVL0,"GAMMALVL0"}, + {&s_GAMMALVL1,"GAMMALVL1"}, + {&s_GAMMALVL2,"GAMMALVL2"}, + {&s_GAMMALVL3,"GAMMALVL3"}, + {&s_GAMMALVL4,"GAMMALVL4"}, + {&s_EMPTYSTRING,"EMPTYSTRING"}, + {&s_GOTARMOR,"GOTARMOR"}, + {&s_GOTMEGA,"GOTMEGA"}, + {&s_GOTHTHBONUS,"GOTHTHBONUS"}, + {&s_GOTARMBONUS,"GOTARMBONUS"}, + {&s_GOTSTIM,"GOTSTIM"}, + {&s_GOTMEDINEED,"GOTMEDINEED"}, + {&s_GOTMEDIKIT,"GOTMEDIKIT"}, + {&s_GOTSUPER,"GOTSUPER"}, + {&s_GOTBLUECARD,"GOTBLUECARD"}, + {&s_GOTYELWCARD,"GOTYELWCARD"}, + {&s_GOTREDCARD,"GOTREDCARD"}, + {&s_GOTBLUESKUL,"GOTBLUESKUL"}, + {&s_GOTYELWSKUL,"GOTYELWSKUL"}, + {&s_GOTREDSKULL,"GOTREDSKULL"}, + {&s_GOTINVUL,"GOTINVUL"}, + {&s_GOTBERSERK,"GOTBERSERK"}, + {&s_GOTINVIS,"GOTINVIS"}, + {&s_GOTSUIT,"GOTSUIT"}, + {&s_GOTMAP,"GOTMAP"}, + {&s_GOTVISOR,"GOTVISOR"}, + {&s_GOTMSPHERE,"GOTMSPHERE"}, + {&s_GOTCLIP,"GOTCLIP"}, + {&s_GOTCLIPBOX,"GOTCLIPBOX"}, + {&s_GOTROCKET,"GOTROCKET"}, + {&s_GOTROCKBOX,"GOTROCKBOX"}, + {&s_GOTCELL,"GOTCELL"}, + {&s_GOTCELLBOX,"GOTCELLBOX"}, + {&s_GOTSHELLS,"GOTSHELLS"}, + {&s_GOTSHELLBOX,"GOTSHELLBOX"}, + {&s_GOTBACKPACK,"GOTBACKPACK"}, + {&s_GOTBFG9000,"GOTBFG9000"}, + {&s_GOTCHAINGUN,"GOTCHAINGUN"}, + {&s_GOTCHAINSAW,"GOTCHAINSAW"}, + {&s_GOTLAUNCHER,"GOTLAUNCHER"}, + {&s_GOTPLASMA,"GOTPLASMA"}, + {&s_GOTSHOTGUN,"GOTSHOTGUN"}, + {&s_GOTSHOTGUN2,"GOTSHOTGUN2"}, + {&s_PD_BLUEO,"PD_BLUEO"}, + {&s_PD_REDO,"PD_REDO"}, + {&s_PD_YELLOWO,"PD_YELLOWO"}, + {&s_PD_BLUEK,"PD_BLUEK"}, + {&s_PD_REDK,"PD_REDK"}, + {&s_PD_YELLOWK,"PD_YELLOWK"}, + {&s_PD_BLUEC,"PD_BLUEC"}, + {&s_PD_REDC,"PD_REDC"}, + {&s_PD_YELLOWC,"PD_YELLOWC"}, + {&s_PD_BLUES,"PD_BLUES"}, + {&s_PD_REDS,"PD_REDS"}, + {&s_PD_YELLOWS,"PD_YELLOWS"}, + {&s_PD_ANY,"PD_ANY"}, + {&s_PD_ALL3,"PD_ALL3"}, + {&s_PD_ALL6,"PD_ALL6"}, + {&s_GGSAVED,"GGSAVED"}, + {&s_HUSTR_MSGU,"HUSTR_MSGU"}, + {&s_HUSTR_E1M1,"HUSTR_E1M1"}, + {&s_HUSTR_E1M2,"HUSTR_E1M2"}, + {&s_HUSTR_E1M3,"HUSTR_E1M3"}, + {&s_HUSTR_E1M4,"HUSTR_E1M4"}, + {&s_HUSTR_E1M5,"HUSTR_E1M5"}, + {&s_HUSTR_E1M6,"HUSTR_E1M6"}, + {&s_HUSTR_E1M7,"HUSTR_E1M7"}, + {&s_HUSTR_E1M8,"HUSTR_E1M8"}, + {&s_HUSTR_E1M9,"HUSTR_E1M9"}, + {&s_HUSTR_E2M1,"HUSTR_E2M1"}, + {&s_HUSTR_E2M2,"HUSTR_E2M2"}, + {&s_HUSTR_E2M3,"HUSTR_E2M3"}, + {&s_HUSTR_E2M4,"HUSTR_E2M4"}, + {&s_HUSTR_E2M5,"HUSTR_E2M5"}, + {&s_HUSTR_E2M6,"HUSTR_E2M6"}, + {&s_HUSTR_E2M7,"HUSTR_E2M7"}, + {&s_HUSTR_E2M8,"HUSTR_E2M8"}, + {&s_HUSTR_E2M9,"HUSTR_E2M9"}, + {&s_HUSTR_E3M1,"HUSTR_E3M1"}, + {&s_HUSTR_E3M2,"HUSTR_E3M2"}, + {&s_HUSTR_E3M3,"HUSTR_E3M3"}, + {&s_HUSTR_E3M4,"HUSTR_E3M4"}, + {&s_HUSTR_E3M5,"HUSTR_E3M5"}, + {&s_HUSTR_E3M6,"HUSTR_E3M6"}, + {&s_HUSTR_E3M7,"HUSTR_E3M7"}, + {&s_HUSTR_E3M8,"HUSTR_E3M8"}, + {&s_HUSTR_E3M9,"HUSTR_E3M9"}, + {&s_HUSTR_E4M1,"HUSTR_E4M1"}, + {&s_HUSTR_E4M2,"HUSTR_E4M2"}, + {&s_HUSTR_E4M3,"HUSTR_E4M3"}, + {&s_HUSTR_E4M4,"HUSTR_E4M4"}, + {&s_HUSTR_E4M5,"HUSTR_E4M5"}, + {&s_HUSTR_E4M6,"HUSTR_E4M6"}, + {&s_HUSTR_E4M7,"HUSTR_E4M7"}, + {&s_HUSTR_E4M8,"HUSTR_E4M8"}, + {&s_HUSTR_E4M9,"HUSTR_E4M9"}, + {&s_HUSTR_1,"HUSTR_1"}, + {&s_HUSTR_2,"HUSTR_2"}, + {&s_HUSTR_3,"HUSTR_3"}, + {&s_HUSTR_4,"HUSTR_4"}, + {&s_HUSTR_5,"HUSTR_5"}, + {&s_HUSTR_6,"HUSTR_6"}, + {&s_HUSTR_7,"HUSTR_7"}, + {&s_HUSTR_8,"HUSTR_8"}, + {&s_HUSTR_9,"HUSTR_9"}, + {&s_HUSTR_10,"HUSTR_10"}, + {&s_HUSTR_11,"HUSTR_11"}, + {&s_HUSTR_12,"HUSTR_12"}, + {&s_HUSTR_13,"HUSTR_13"}, + {&s_HUSTR_14,"HUSTR_14"}, + {&s_HUSTR_15,"HUSTR_15"}, + {&s_HUSTR_16,"HUSTR_16"}, + {&s_HUSTR_17,"HUSTR_17"}, + {&s_HUSTR_18,"HUSTR_18"}, + {&s_HUSTR_19,"HUSTR_19"}, + {&s_HUSTR_20,"HUSTR_20"}, + {&s_HUSTR_21,"HUSTR_21"}, + {&s_HUSTR_22,"HUSTR_22"}, + {&s_HUSTR_23,"HUSTR_23"}, + {&s_HUSTR_24,"HUSTR_24"}, + {&s_HUSTR_25,"HUSTR_25"}, + {&s_HUSTR_26,"HUSTR_26"}, + {&s_HUSTR_27,"HUSTR_27"}, + {&s_HUSTR_28,"HUSTR_28"}, + {&s_HUSTR_29,"HUSTR_29"}, + {&s_HUSTR_30,"HUSTR_30"}, + {&s_HUSTR_31,"HUSTR_31"}, + {&s_HUSTR_32,"HUSTR_32"}, + {&s_HUSTR_33,"HUSTR_33"}, + {&s_PHUSTR_1,"PHUSTR_1"}, + {&s_PHUSTR_2,"PHUSTR_2"}, + {&s_PHUSTR_3,"PHUSTR_3"}, + {&s_PHUSTR_4,"PHUSTR_4"}, + {&s_PHUSTR_5,"PHUSTR_5"}, + {&s_PHUSTR_6,"PHUSTR_6"}, + {&s_PHUSTR_7,"PHUSTR_7"}, + {&s_PHUSTR_8,"PHUSTR_8"}, + {&s_PHUSTR_9,"PHUSTR_9"}, + {&s_PHUSTR_10,"PHUSTR_10"}, + {&s_PHUSTR_11,"PHUSTR_11"}, + {&s_PHUSTR_12,"PHUSTR_12"}, + {&s_PHUSTR_13,"PHUSTR_13"}, + {&s_PHUSTR_14,"PHUSTR_14"}, + {&s_PHUSTR_15,"PHUSTR_15"}, + {&s_PHUSTR_16,"PHUSTR_16"}, + {&s_PHUSTR_17,"PHUSTR_17"}, + {&s_PHUSTR_18,"PHUSTR_18"}, + {&s_PHUSTR_19,"PHUSTR_19"}, + {&s_PHUSTR_20,"PHUSTR_20"}, + {&s_PHUSTR_21,"PHUSTR_21"}, + {&s_PHUSTR_22,"PHUSTR_22"}, + {&s_PHUSTR_23,"PHUSTR_23"}, + {&s_PHUSTR_24,"PHUSTR_24"}, + {&s_PHUSTR_25,"PHUSTR_25"}, + {&s_PHUSTR_26,"PHUSTR_26"}, + {&s_PHUSTR_27,"PHUSTR_27"}, + {&s_PHUSTR_28,"PHUSTR_28"}, + {&s_PHUSTR_29,"PHUSTR_29"}, + {&s_PHUSTR_30,"PHUSTR_30"}, + {&s_PHUSTR_31,"PHUSTR_31"}, + {&s_PHUSTR_32,"PHUSTR_32"}, + {&s_THUSTR_1,"THUSTR_1"}, + {&s_THUSTR_2,"THUSTR_2"}, + {&s_THUSTR_3,"THUSTR_3"}, + {&s_THUSTR_4,"THUSTR_4"}, + {&s_THUSTR_5,"THUSTR_5"}, + {&s_THUSTR_6,"THUSTR_6"}, + {&s_THUSTR_7,"THUSTR_7"}, + {&s_THUSTR_8,"THUSTR_8"}, + {&s_THUSTR_9,"THUSTR_9"}, + {&s_THUSTR_10,"THUSTR_10"}, + {&s_THUSTR_11,"THUSTR_11"}, + {&s_THUSTR_12,"THUSTR_12"}, + {&s_THUSTR_13,"THUSTR_13"}, + {&s_THUSTR_14,"THUSTR_14"}, + {&s_THUSTR_15,"THUSTR_15"}, + {&s_THUSTR_16,"THUSTR_16"}, + {&s_THUSTR_17,"THUSTR_17"}, + {&s_THUSTR_18,"THUSTR_18"}, + {&s_THUSTR_19,"THUSTR_19"}, + {&s_THUSTR_20,"THUSTR_20"}, + {&s_THUSTR_21,"THUSTR_21"}, + {&s_THUSTR_22,"THUSTR_22"}, + {&s_THUSTR_23,"THUSTR_23"}, + {&s_THUSTR_24,"THUSTR_24"}, + {&s_THUSTR_25,"THUSTR_25"}, + {&s_THUSTR_26,"THUSTR_26"}, + {&s_THUSTR_27,"THUSTR_27"}, + {&s_THUSTR_28,"THUSTR_28"}, + {&s_THUSTR_29,"THUSTR_29"}, + {&s_THUSTR_30,"THUSTR_30"}, + {&s_THUSTR_31,"THUSTR_31"}, + {&s_THUSTR_32,"THUSTR_32"}, + {&s_HUSTR_CHATMACRO1,"HUSTR_CHATMACRO1"}, + {&s_HUSTR_CHATMACRO2,"HUSTR_CHATMACRO2"}, + {&s_HUSTR_CHATMACRO3,"HUSTR_CHATMACRO3"}, + {&s_HUSTR_CHATMACRO4,"HUSTR_CHATMACRO4"}, + {&s_HUSTR_CHATMACRO5,"HUSTR_CHATMACRO5"}, + {&s_HUSTR_CHATMACRO6,"HUSTR_CHATMACRO6"}, + {&s_HUSTR_CHATMACRO7,"HUSTR_CHATMACRO7"}, + {&s_HUSTR_CHATMACRO8,"HUSTR_CHATMACRO8"}, + {&s_HUSTR_CHATMACRO9,"HUSTR_CHATMACRO9"}, + {&s_HUSTR_CHATMACRO0,"HUSTR_CHATMACRO0"}, + {&s_HUSTR_TALKTOSELF1,"HUSTR_TALKTOSELF1"}, + {&s_HUSTR_TALKTOSELF2,"HUSTR_TALKTOSELF2"}, + {&s_HUSTR_TALKTOSELF3,"HUSTR_TALKTOSELF3"}, + {&s_HUSTR_TALKTOSELF4,"HUSTR_TALKTOSELF4"}, + {&s_HUSTR_TALKTOSELF5,"HUSTR_TALKTOSELF5"}, + {&s_HUSTR_MESSAGESENT,"HUSTR_MESSAGESENT"}, + {&s_HUSTR_PLRGREEN,"HUSTR_PLRGREEN"}, + {&s_HUSTR_PLRINDIGO,"HUSTR_PLRINDIGO"}, + {&s_HUSTR_PLRBROWN,"HUSTR_PLRBROWN"}, + {&s_HUSTR_PLRRED,"HUSTR_PLRRED"}, + //{c_HUSTR_KEYGREEN,"HUSTR_KEYGREEN"}, + //{c_HUSTR_KEYINDIGO,"HUSTR_KEYINDIGO"}, + //{c_HUSTR_KEYBROWN,"HUSTR_KEYBROWN"}, + //{c_HUSTR_KEYRED,"HUSTR_KEYRED"}, + {&s_AMSTR_FOLLOWON,"AMSTR_FOLLOWON"}, + {&s_AMSTR_FOLLOWOFF,"AMSTR_FOLLOWOFF"}, + {&s_AMSTR_GRIDON,"AMSTR_GRIDON"}, + {&s_AMSTR_GRIDOFF,"AMSTR_GRIDOFF"}, + {&s_AMSTR_MARKEDSPOT,"AMSTR_MARKEDSPOT"}, + {&s_AMSTR_MARKSCLEARED,"AMSTR_MARKSCLEARED"}, + {&s_STSTR_MUS,"STSTR_MUS"}, + {&s_STSTR_NOMUS,"STSTR_NOMUS"}, + {&s_STSTR_DQDON,"STSTR_DQDON"}, + {&s_STSTR_DQDOFF,"STSTR_DQDOFF"}, + {&s_STSTR_KFAADDED,"STSTR_KFAADDED"}, + {&s_STSTR_FAADDED,"STSTR_FAADDED"}, + {&s_STSTR_NCON,"STSTR_NCON"}, + {&s_STSTR_NCOFF,"STSTR_NCOFF"}, + {&s_STSTR_BEHOLD,"STSTR_BEHOLD"}, + {&s_STSTR_BEHOLDX,"STSTR_BEHOLDX"}, + {&s_STSTR_CHOPPERS,"STSTR_CHOPPERS"}, + {&s_STSTR_CLEV,"STSTR_CLEV"}, + {&s_STSTR_COMPON,"STSTR_COMPON"}, + {&s_STSTR_COMPOFF,"STSTR_COMPOFF"}, + {&s_E1TEXT,"E1TEXT"}, + {&s_E2TEXT,"E2TEXT"}, + {&s_E3TEXT,"E3TEXT"}, + {&s_E4TEXT,"E4TEXT"}, + {&s_C1TEXT,"C1TEXT"}, + {&s_C2TEXT,"C2TEXT"}, + {&s_C3TEXT,"C3TEXT"}, + {&s_C4TEXT,"C4TEXT"}, + {&s_C5TEXT,"C5TEXT"}, + {&s_C6TEXT,"C6TEXT"}, + {&s_P1TEXT,"P1TEXT"}, + {&s_P2TEXT,"P2TEXT"}, + {&s_P3TEXT,"P3TEXT"}, + {&s_P4TEXT,"P4TEXT"}, + {&s_P5TEXT,"P5TEXT"}, + {&s_P6TEXT,"P6TEXT"}, + {&s_T1TEXT,"T1TEXT"}, + {&s_T2TEXT,"T2TEXT"}, + {&s_T3TEXT,"T3TEXT"}, + {&s_T4TEXT,"T4TEXT"}, + {&s_T5TEXT,"T5TEXT"}, + {&s_T6TEXT,"T6TEXT"}, + {&s_CC_ZOMBIE,"CC_ZOMBIE"}, + {&s_CC_SHOTGUN,"CC_SHOTGUN"}, + {&s_CC_HEAVY,"CC_HEAVY"}, + {&s_CC_IMP,"CC_IMP"}, + {&s_CC_DEMON,"CC_DEMON"}, + {&s_CC_LOST,"CC_LOST"}, + {&s_CC_CACO,"CC_CACO"}, + {&s_CC_HELL,"CC_HELL"}, + {&s_CC_BARON,"CC_BARON"}, + {&s_CC_ARACH,"CC_ARACH"}, + {&s_CC_PAIN,"CC_PAIN"}, + {&s_CC_REVEN,"CC_REVEN"}, + {&s_CC_MANCU,"CC_MANCU"}, + {&s_CC_ARCH,"CC_ARCH"}, + {&s_CC_SPIDER,"CC_SPIDER"}, + {&s_CC_CYBER,"CC_CYBER"}, + {&s_CC_HERO,"CC_HERO"}, + {&bgflatE1,"BGFLATE1"}, + {&bgflatE2,"BGFLATE2"}, + {&bgflatE3,"BGFLATE3"}, + {&bgflatE4,"BGFLATE4"}, + {&bgflat06,"BGFLAT06"}, + {&bgflat11,"BGFLAT11"}, + {&bgflat20,"BGFLAT20"}, + {&bgflat30,"BGFLAT30"}, + {&bgflat15,"BGFLAT15"}, + {&bgflat31,"BGFLAT31"}, + {&bgcastcall,"BGCASTCALL"}, + // Ty 04/08/98 - added 5 general purpose startup announcement + // strings for hacker use. See m_menu.c + {&startup1,"STARTUP1"}, + {&startup2,"STARTUP2"}, + {&startup3,"STARTUP3"}, + {&startup4,"STARTUP4"}, + {&startup5,"STARTUP5"}, + {&savegamename,"SAVEGAMENAME"}, // Ty 05/03/98 +}; + +static int deh_numstrlookup = +sizeof(deh_strlookup)/sizeof(deh_strlookup[0]); + +const char *deh_newlevel = "NEWLEVEL"; // CPhipps - const + +// DOOM shareware/registered/retail (Ultimate) names. +// CPhipps - const**const +const char **const mapnames[] = +{ + &s_HUSTR_E1M1, + &s_HUSTR_E1M2, + &s_HUSTR_E1M3, + &s_HUSTR_E1M4, + &s_HUSTR_E1M5, + &s_HUSTR_E1M6, + &s_HUSTR_E1M7, + &s_HUSTR_E1M8, + &s_HUSTR_E1M9, + + &s_HUSTR_E2M1, + &s_HUSTR_E2M2, + &s_HUSTR_E2M3, + &s_HUSTR_E2M4, + &s_HUSTR_E2M5, + &s_HUSTR_E2M6, + &s_HUSTR_E2M7, + &s_HUSTR_E2M8, + &s_HUSTR_E2M9, + + &s_HUSTR_E3M1, + &s_HUSTR_E3M2, + &s_HUSTR_E3M3, + &s_HUSTR_E3M4, + &s_HUSTR_E3M5, + &s_HUSTR_E3M6, + &s_HUSTR_E3M7, + &s_HUSTR_E3M8, + &s_HUSTR_E3M9, + + &s_HUSTR_E4M1, + &s_HUSTR_E4M2, + &s_HUSTR_E4M3, + &s_HUSTR_E4M4, + &s_HUSTR_E4M5, + &s_HUSTR_E4M6, + &s_HUSTR_E4M7, + &s_HUSTR_E4M8, + &s_HUSTR_E4M9, + + &deh_newlevel, // spares? Unused. + &deh_newlevel, + &deh_newlevel, + &deh_newlevel, + &deh_newlevel, + &deh_newlevel, + &deh_newlevel, + &deh_newlevel, + &deh_newlevel +}; + +// CPhipps - const**const +const char **const mapnames2[] = // DOOM 2 map names. +{ + &s_HUSTR_1, + &s_HUSTR_2, + &s_HUSTR_3, + &s_HUSTR_4, + &s_HUSTR_5, + &s_HUSTR_6, + &s_HUSTR_7, + &s_HUSTR_8, + &s_HUSTR_9, + &s_HUSTR_10, + &s_HUSTR_11, + + &s_HUSTR_12, + &s_HUSTR_13, + &s_HUSTR_14, + &s_HUSTR_15, + &s_HUSTR_16, + &s_HUSTR_17, + &s_HUSTR_18, + &s_HUSTR_19, + &s_HUSTR_20, + + &s_HUSTR_21, + &s_HUSTR_22, + &s_HUSTR_23, + &s_HUSTR_24, + &s_HUSTR_25, + &s_HUSTR_26, + &s_HUSTR_27, + &s_HUSTR_28, + &s_HUSTR_29, + &s_HUSTR_30, + &s_HUSTR_31, + &s_HUSTR_32, + &s_HUSTR_33, +}; + +// CPhipps - const**const +const char **const mapnamesp[] = // Plutonia WAD map names. +{ + &s_PHUSTR_1, + &s_PHUSTR_2, + &s_PHUSTR_3, + &s_PHUSTR_4, + &s_PHUSTR_5, + &s_PHUSTR_6, + &s_PHUSTR_7, + &s_PHUSTR_8, + &s_PHUSTR_9, + &s_PHUSTR_10, + &s_PHUSTR_11, + + &s_PHUSTR_12, + &s_PHUSTR_13, + &s_PHUSTR_14, + &s_PHUSTR_15, + &s_PHUSTR_16, + &s_PHUSTR_17, + &s_PHUSTR_18, + &s_PHUSTR_19, + &s_PHUSTR_20, + + &s_PHUSTR_21, + &s_PHUSTR_22, + &s_PHUSTR_23, + &s_PHUSTR_24, + &s_PHUSTR_25, + &s_PHUSTR_26, + &s_PHUSTR_27, + &s_PHUSTR_28, + &s_PHUSTR_29, + &s_PHUSTR_30, + &s_PHUSTR_31, + &s_PHUSTR_32, +}; + +// CPhipps - const**const +const char **const mapnamest[] = // TNT WAD map names. +{ + &s_THUSTR_1, + &s_THUSTR_2, + &s_THUSTR_3, + &s_THUSTR_4, + &s_THUSTR_5, + &s_THUSTR_6, + &s_THUSTR_7, + &s_THUSTR_8, + &s_THUSTR_9, + &s_THUSTR_10, + &s_THUSTR_11, + + &s_THUSTR_12, + &s_THUSTR_13, + &s_THUSTR_14, + &s_THUSTR_15, + &s_THUSTR_16, + &s_THUSTR_17, + &s_THUSTR_18, + &s_THUSTR_19, + &s_THUSTR_20, + + &s_THUSTR_21, + &s_THUSTR_22, + &s_THUSTR_23, + &s_THUSTR_24, + &s_THUSTR_25, + &s_THUSTR_26, + &s_THUSTR_27, + &s_THUSTR_28, + &s_THUSTR_29, + &s_THUSTR_30, + &s_THUSTR_31, + &s_THUSTR_32, +}; + +// Function prototypes +void lfstrip(char *); // strip the \r and/or \n off of a line +void rstrip(char *); // strip trailing whitespace +char * ptr_lstrip(char *); // point past leading whitespace +dboolean deh_GetData(char *, char *, uint_64_t *, char **, FILE *); +dboolean deh_procStringSub(char *, char *, char *, FILE *); +char * dehReformatStr(char *); + +// Prototypes for block processing functions +// Pointers to these functions are used as the blocks are encountered. + +static void deh_procThing(DEHFILE *fpin, FILE* fpout, char *line); +static void deh_procFrame(DEHFILE *, FILE*, char *); +static void deh_procPointer(DEHFILE *, FILE*, char *); +static void deh_procSounds(DEHFILE *, FILE*, char *); +static void deh_procAmmo(DEHFILE *, FILE*, char *); +static void deh_procWeapon(DEHFILE *, FILE*, char *); +static void deh_procSprite(DEHFILE *, FILE*, char *); +static void deh_procCheat(DEHFILE *, FILE*, char *); +static void deh_procMisc(DEHFILE *, FILE*, char *); +static void deh_procText(DEHFILE *, FILE*, char *); +static void deh_procPars(DEHFILE *, FILE*, char *); +static void deh_procStrings(DEHFILE *, FILE*, char *); +static void deh_procError(DEHFILE *, FILE*, char *); +static void deh_procBexCodePointers(DEHFILE *, FILE*, char *); +static void deh_procHelperThing(DEHFILE *, FILE *, char *); // haleyjd 9/22/99 +// haleyjd: handlers to fully deprecate the DeHackEd text section +static void deh_procBexSounds(DEHFILE *, FILE *, char *); +static void deh_procBexMusic(DEHFILE *, FILE *, char *); +static void deh_procBexSprites(DEHFILE *, FILE *, char *); + +// Structure deh_block is used to hold the block names that can +// be encountered, and the routines to use to decipher them + +typedef struct +{ + const char *key; // a mnemonic block code name // CPhipps - const* + void (*const fptr)(DEHFILE *, FILE*, char *); // handler +} deh_block; + +#define DEH_BUFFERMAX 1024 // input buffer area size, hardcodedfor now +// killough 8/9/98: make DEH_BLOCKMAX self-adjusting +#define DEH_BLOCKMAX (sizeof deh_blocks/sizeof*deh_blocks) // size of array +#define DEH_MAXKEYLEN 32 // as much of any key as we'll look at +#define DEH_MOBJINFOMAX 26 // number of ints in the mobjinfo_t structure (!) + +// Put all the block header values, and the function to be called when that +// one is encountered, in this array: +static const deh_block deh_blocks[] = { // CPhipps - static const + /* 0 */ {"Thing",deh_procThing}, + /* 1 */ {"Frame",deh_procFrame}, + /* 2 */ {"Pointer",deh_procPointer}, + /* 3 */ {"Sound",deh_procSounds}, // Ty 03/16/98 corrected from "Sounds" + /* 4 */ {"Ammo",deh_procAmmo}, + /* 5 */ {"Weapon",deh_procWeapon}, + /* 6 */ {"Sprite",deh_procSprite}, + /* 7 */ {"Cheat",deh_procCheat}, + /* 8 */ {"Misc",deh_procMisc}, + /* 9 */ {"Text",deh_procText}, // -- end of standard "deh" entries, + + // begin BOOM Extensions (BEX) + + /* 10 */ {"[STRINGS]",deh_procStrings}, // new string changes + /* 11 */ {"[PARS]",deh_procPars}, // alternative block marker + /* 12 */ {"[CODEPTR]",deh_procBexCodePointers}, // bex codepointers by mnemonic + /* 13 */ {"[HELPER]", deh_procHelperThing}, // helper thing substitution haleyjd 9/22/99 + /* 14 */ {"[SPRITES]", deh_procBexSprites}, // bex style sprites + /* 15 */ {"[SOUNDS]", deh_procBexSounds}, // bex style sounds + /* 16 */ {"[MUSIC]", deh_procBexMusic}, // bex style music + /* 17 */ {"", deh_procError} // dummy to handle anything else +}; + +// flag to skip included deh-style text, used with INCLUDE NOTEXT directive +static dboolean includenotext = false; + +// MOBJINFO - Dehacked block name = "Thing" +// Usage: Thing nn (name) +// These are for mobjinfo_t types. Each is an integer +// within the structure, so we can use index of the string in this +// array to offset by sizeof(int) into the mobjinfo_t array at [nn] +// * things are base zero but dehacked considers them to start at #1. *** +// CPhipps - static const + +static const char *deh_mobjinfo[DEH_MOBJINFOMAX] = +{ + "ID #", // .doomednum + "Initial frame", // .spawnstate + "Hit points", // .spawnhealth + "First moving frame", // .seestate + "Alert sound", // .seesound + "Reaction time", // .reactiontime + "Attack sound", // .attacksound + "Injury frame", // .painstate + "Pain chance", // .painchance + "Pain sound", // .painsound + "Close attack frame", // .meleestate + "Far attack frame", // .missilestate + "Death frame", // .deathstate + "Exploding frame", // .xdeathstate + "Death sound", // .deathsound + "Speed", // .speed + "Width", // .radius + "Height", // .height + "Mass", // .mass + "Missile damage", // .damage + "Action sound", // .activesound + "Bits", // .flags + "Bits2", // .flags + "Respawn frame", // .raisestate + "Dropped item", // .droppeditem + "Blood color", // .bloodcolor +}; + +// Strings that are used to indicate flags ("Bits" in mobjinfo) +// This is an array of bit masks that are related to p_mobj.h +// values, using the smae names without the MF_ in front. +// Ty 08/27/98 new code +// +// killough 10/98: +// +// Convert array to struct to allow multiple values, make array size variable + +#define DEH_MOBJFLAGMAX (sizeof deh_mobjflags/sizeof*deh_mobjflags) + +struct deh_mobjflags_s { + const char *name; // CPhipps - const* + uint_64_t value; +}; + +// CPhipps - static const +static const struct deh_mobjflags_s deh_mobjflags[] = { + {"SPECIAL", MF_SPECIAL}, // call P_Specialthing when touched + {"SOLID", MF_SOLID}, // block movement + {"SHOOTABLE", MF_SHOOTABLE}, // can be hit + {"NOSECTOR", MF_NOSECTOR}, // invisible but touchable + {"NOBLOCKMAP", MF_NOBLOCKMAP}, // inert but displayable + {"AMBUSH", MF_AMBUSH}, // deaf monster + {"JUSTHIT", MF_JUSTHIT}, // will try to attack right back + {"JUSTATTACKED", MF_JUSTATTACKED}, // take at least 1 step before attacking + {"SPAWNCEILING", MF_SPAWNCEILING}, // initially hang from ceiling + {"NOGRAVITY", MF_NOGRAVITY}, // don't apply gravity during play + {"DROPOFF", MF_DROPOFF}, // can jump from high places + {"PICKUP", MF_PICKUP}, // will pick up items + {"NOCLIP", MF_NOCLIP}, // goes through walls + {"SLIDE", MF_SLIDE}, // keep info about sliding along walls + {"FLOAT", MF_FLOAT}, // allow movement to any height + {"TELEPORT", MF_TELEPORT}, // don't cross lines or look at heights + {"MISSILE", MF_MISSILE}, // don't hit same species, explode on block + {"DROPPED", MF_DROPPED}, // dropped, not spawned (like ammo clip) + {"SHADOW", MF_SHADOW}, // use fuzzy draw like spectres + {"NOBLOOD", MF_NOBLOOD}, // puffs instead of blood when shot + {"CORPSE", MF_CORPSE}, // so it will slide down steps when dead + {"INFLOAT", MF_INFLOAT}, // float but not to target height + {"COUNTKILL", MF_COUNTKILL}, // count toward the kills total + {"COUNTITEM", MF_COUNTITEM}, // count toward the items total + {"SKULLFLY", MF_SKULLFLY}, // special handling for flying skulls + {"NOTDMATCH", MF_NOTDMATCH}, // do not spawn in deathmatch + + // killough 10/98: TRANSLATION consists of 2 bits, not 1: + + {"TRANSLATION", MF_TRANSLATION1}, // for Boom bug-compatibility + {"TRANSLATION1", MF_TRANSLATION1}, // use translation table for color (players) + {"TRANSLATION2", MF_TRANSLATION2}, // use translation table for color (players) + {"UNUSED1", MF_TRANSLATION2}, // unused bit # 1 -- For Boom bug-compatibility + {"UNUSED2", MF_UNUSED2}, // unused bit # 2 -- For Boom compatibility + {"UNUSED3", MF_UNUSED3}, // unused bit # 3 -- For Boom compatibility + {"UNUSED4", MF_TRANSLUCENT}, // unused bit # 4 -- For Boom compatibility + {"TRANSLUCENT", MF_TRANSLUCENT}, // apply translucency to sprite (BOOM) + {"TOUCHY", MF_TOUCHY}, // dies on contact with solid objects (MBF) + {"BOUNCES", MF_BOUNCES}, // bounces off floors, ceilings and maybe walls (MBF) + {"FRIEND", MF_FRIEND}, // a friend of the player(s) (MBF) +}; + +// STATE - Dehacked block name = "Frame" and "Pointer" +// Usage: Frame nn +// Usage: Pointer nn (Frame nn) +// These are indexed separately, for lookup to the actual +// function pointers. Here we'll take whatever Dehacked gives +// us and go from there. The (Frame nn) after the pointer is the +// real place to put this value. The "Pointer" value is an xref +// that Dehacked uses and is useless to us. +// * states are base zero and have a dummy #0 (TROO) + +static const char *deh_state[] = // CPhipps - static const* +{ + "Sprite number", // .sprite (spritenum_t) // an enum + "Sprite subnumber", // .frame (long) + "Duration", // .tics (long) + "Next frame", // .nextstate (statenum_t) + // This is set in a separate "Pointer" block from Dehacked + "Codep Frame", // pointer to first use of action (actionf_t) + "Unknown 1", // .misc1 (long) + "Unknown 2" // .misc2 (long) +}; + +// SFXINFO_STRUCT - Dehacked block name = "Sounds" +// Sound effects, typically not changed (redirected, and new sfx put +// into the pwad, but not changed here. Can you tell that Gregdidn't +// know what they were for, mostly? Can you tell that I don't either? +// Mostly I just put these into the same slots as they are in the struct. +// This may not be supported in our -deh option if it doesn't make sense by then. + +// * sounds are base zero but have a dummy #0 + +static const char *deh_sfxinfo[] = // CPhipps - static const* +{ + "Offset", // pointer to a name string, changed in text + "Zero/One", // .singularity (int, one at a time flag) + "Value", // .priority + "Zero 1", // .link (sfxinfo_t*) referenced sound if linked + "Zero 2", // .pitch + "Zero 3", // .volume + "Zero 4", // .data (SAMPLE*) sound data + "Neg. One 1", // .usefulness + "Neg. One 2" // .lumpnum +}; + +// MUSICINFO is not supported in Dehacked. Ignored here. +// * music entries are base zero but have a dummy #0 + +// SPRITE - Dehacked block name = "Sprite" +// Usage = Sprite nn +// Sprite redirection by offset into the text area - unsupported by BOOM +// * sprites are base zero and dehacked uses it that way. + +// static const char *deh_sprite[] = // CPhipps - static const* +// { +// "Offset" // supposed to be the offset into the text section +// }; + +// AMMO - Dehacked block name = "Ammo" +// usage = Ammo n (name) +// Ammo information for the few types of ammo + +static const char *deh_ammo[] = // CPhipps - static const* +{ + "Max ammo", // maxammo[] + "Per ammo" // clipammo[] +}; + +// WEAPONS - Dehacked block name = "Weapon" +// Usage: Weapon nn (name) +// Basically a list of frames and what kind of ammo (see above)it uses. + +static const char *deh_weapon[] = // CPhipps - static const* +{ + "Ammo type", // .ammo + "Deselect frame", // .upstate + "Select frame", // .downstate + "Bobbing frame", // .readystate + "Shooting frame", // .atkstate + "Firing frame" // .flashstate +}; + +// CHEATS - Dehacked block name = "Cheat" +// Usage: Cheat 0 +// Always uses a zero in the dehacked file, for consistency. No meaning. +// These are just plain funky terms compared with id's +// +// killough 4/18/98: integrated into main cheat table now (see st_stuff.c) + +// MISC - Dehacked block name = "Misc" +// Usage: Misc 0 +// Always uses a zero in the dehacked file, for consistency. No meaning. + +static const char *deh_misc[] = // CPhipps - static const* +{ + "Initial Health", // initial_health + "Initial Bullets", // initial_bullets + "Max Health", // maxhealth + "Max Armor", // max_armor + "Green Armor Class", // green_armor_class + "Blue Armor Class", // blue_armor_class + "Max Soulsphere", // max_soul + "Soulsphere Health", // soul_health + "Megasphere Health", // mega_health + "God Mode Health", // god_health + "IDFA Armor", // idfa_armor + "IDFA Armor Class", // idfa_armor_class + "IDKFA Armor", // idkfa_armor + "IDKFA Armor Class", // idkfa_armor_class + "BFG Cells/Shot", // BFGCELLS + "Monsters Infight" // Unknown--not a specific number it seems, but + // the logic has to be here somewhere or + // it'd happen always +}; + +// TEXT - Dehacked block name = "Text" +// Usage: Text fromlen tolen +// Dehacked allows a bit of adjustment to the length (why?) + +// BEX extension [CODEPTR] +// Usage: Start block, then each line is: +// FRAME nnn = PointerMnemonic + +typedef struct { + actionf_t cptr; // actual pointer to the subroutine + const char *lookup; // mnemonic lookup string to be specified in BEX + // CPhipps - const* +} deh_bexptr; + +static const deh_bexptr deh_bexptrs[] = // CPhipps - static const +{ + {A_Light0, "A_Light0"}, + {A_WeaponReady, "A_WeaponReady"}, + {A_Lower, "A_Lower"}, + {A_Raise, "A_Raise"}, + {A_Punch, "A_Punch"}, + {A_ReFire, "A_ReFire"}, + {A_FirePistol, "A_FirePistol"}, + {A_Light1, "A_Light1"}, + {A_FireShotgun, "A_FireShotgun"}, + {A_Light2, "A_Light2"}, + {A_FireShotgun2, "A_FireShotgun2"}, + {A_CheckReload, "A_CheckReload"}, + {A_OpenShotgun2, "A_OpenShotgun2"}, + {A_LoadShotgun2, "A_LoadShotgun2"}, + {A_CloseShotgun2, "A_CloseShotgun2"}, + {A_FireCGun, "A_FireCGun"}, + {A_GunFlash, "A_GunFlash"}, + {A_FireMissile, "A_FireMissile"}, + {A_Saw, "A_Saw"}, + {A_FirePlasma, "A_FirePlasma"}, + {A_BFGsound, "A_BFGsound"}, + {A_FireBFG, "A_FireBFG"}, + {A_BFGSpray, "A_BFGSpray"}, + {A_Explode, "A_Explode"}, + {A_Pain, "A_Pain"}, + {A_PlayerScream, "A_PlayerScream"}, + {A_Fall, "A_Fall"}, + {A_XScream, "A_XScream"}, + {A_Look, "A_Look"}, + {A_Chase, "A_Chase"}, + {A_FaceTarget, "A_FaceTarget"}, + {A_PosAttack, "A_PosAttack"}, + {A_Scream, "A_Scream"}, + {A_SPosAttack, "A_SPosAttack"}, + {A_VileChase, "A_VileChase"}, + {A_VileStart, "A_VileStart"}, + {A_VileTarget, "A_VileTarget"}, + {A_VileAttack, "A_VileAttack"}, + {A_StartFire, "A_StartFire"}, + {A_Fire, "A_Fire"}, + {A_FireCrackle, "A_FireCrackle"}, + {A_Tracer, "A_Tracer"}, + {A_SkelWhoosh, "A_SkelWhoosh"}, + {A_SkelFist, "A_SkelFist"}, + {A_SkelMissile, "A_SkelMissile"}, + {A_FatRaise, "A_FatRaise"}, + {A_FatAttack1, "A_FatAttack1"}, + {A_FatAttack2, "A_FatAttack2"}, + {A_FatAttack3, "A_FatAttack3"}, + {A_BossDeath, "A_BossDeath"}, + {A_CPosAttack, "A_CPosAttack"}, + {A_CPosRefire, "A_CPosRefire"}, + {A_TroopAttack, "A_TroopAttack"}, + {A_SargAttack, "A_SargAttack"}, + {A_HeadAttack, "A_HeadAttack"}, + {A_BruisAttack, "A_BruisAttack"}, + {A_SkullAttack, "A_SkullAttack"}, + {A_Metal, "A_Metal"}, + {A_SpidRefire, "A_SpidRefire"}, + {A_BabyMetal, "A_BabyMetal"}, + {A_BspiAttack, "A_BspiAttack"}, + {A_Hoof, "A_Hoof"}, + {A_CyberAttack, "A_CyberAttack"}, + {A_PainAttack, "A_PainAttack"}, + {A_PainDie, "A_PainDie"}, + {A_KeenDie, "A_KeenDie"}, + {A_BrainPain, "A_BrainPain"}, + {A_BrainScream, "A_BrainScream"}, + {A_BrainDie, "A_BrainDie"}, + {A_BrainAwake, "A_BrainAwake"}, + {A_BrainSpit, "A_BrainSpit"}, + {A_SpawnSound, "A_SpawnSound"}, + {A_SpawnFly, "A_SpawnFly"}, + {A_BrainExplode, "A_BrainExplode"}, + {A_Detonate, "A_Detonate"}, // killough 8/9/98 + {A_Mushroom, "A_Mushroom"}, // killough 10/98 + {A_Die, "A_Die"}, // killough 11/98 + {A_Spawn, "A_Spawn"}, // killough 11/98 + {A_Turn, "A_Turn"}, // killough 11/98 + {A_Face, "A_Face"}, // killough 11/98 + {A_Scratch, "A_Scratch"}, // killough 11/98 + {A_PlaySound, "A_PlaySound"}, // killough 11/98 + {A_RandomJump, "A_RandomJump"}, // killough 11/98 + {A_LineEffect, "A_LineEffect"}, // killough 11/98 + + {A_FireOldBFG, "A_FireOldBFG"}, // killough 7/19/98: classic BFG firing function + {A_BetaSkullAttack, "A_BetaSkullAttack"}, // killough 10/98: beta lost souls attacked different + {A_Stop, "A_Stop"}, + + // This NULL entry must be the last in the list + {NULL, "A_NULL"}, // Ty 05/16/98 +}; + +// to hold startup code pointers from INFO.C +// CPhipps - static +static actionf_t deh_codeptr[NUMSTATES]; + +// haleyjd: support for BEX SPRITES, SOUNDS, and MUSIC +char *deh_spritenames[NUMSPRITES + 1]; +char *deh_musicnames[NUMMUSIC + 1]; +char *deh_soundnames[NUMSFX + 1]; + +void D_BuildBEXTables(void) +{ + int i; + + // moved from ProcessDehFile, then we don't need the static int i + for (i = 0; i < EXTRASTATES; i++) // remember what they start as for deh xref + deh_codeptr[i] = states[i].action; + + // initialize extra dehacked states + for ( ; i < NUMSTATES; i++) + { + states[i].sprite = SPR_TNT1; + states[i].frame = 0; + states[i].tics = -1; + states[i].action = NULL; + states[i].nextstate = i; + states[i].misc1 = 0; + states[i].misc2 = 0; + deh_codeptr[i] = states[i].action; + } + + for(i = 0; i < NUMSPRITES; i++) + deh_spritenames[i] = strdup(sprnames[i]); + deh_spritenames[NUMSPRITES] = NULL; + + for(i = 1; i < NUMMUSIC; i++) + deh_musicnames[i] = strdup(S_music[i].name); + deh_musicnames[0] = deh_musicnames[NUMMUSIC] = NULL; + + for(i = 1; i < NUMSFX; i++) { + if (S_sfx[i].name != NULL) { + deh_soundnames[i] = strdup(S_sfx[i].name); + } else { // This is possible due to how DEHEXTRA has turned S_sfx into a sparse array + deh_soundnames[i] = NULL; + } + } + deh_soundnames[0] = deh_soundnames[NUMSFX] = NULL; + + // ferk: initialize Thing extra properties (keeping vanilla props in info.c) + for (i = 0; i < NUMMOBJTYPES; i++) + { + // mobj id for item dropped on death + switch (i) + { + case MT_WOLFSS: + case MT_POSSESSED: + mobjinfo[i].droppeditem = MT_CLIP; + break; + case MT_SHOTGUY: + mobjinfo[i].droppeditem = MT_SHOTGUN; + break; + case MT_CHAINGUY: + mobjinfo[i].droppeditem = MT_CHAINGUN; + break; + default: + mobjinfo[i].droppeditem = MT_NULL; + } + + // [FG] colored blood and gibs + switch (i) + { + case MT_HEAD: + mobjinfo[i].bloodcolor = 3; // Blue + break; + case MT_BRUISER: + case MT_KNIGHT: + mobjinfo[i].bloodcolor = 2; // Green + break; + default: + mobjinfo[i].bloodcolor = 0; // Red (normal) + } + } +} + +int deh_maxhealth; +int deh_max_soul; +int deh_mega_health; + +dboolean IsDehMaxHealth = false; +dboolean IsDehMaxSoul = false; +dboolean IsDehMegaHealth = false; +dboolean DEH_mobjinfo_bits[NUMMOBJTYPES] = {0}; + +void deh_changeCompTranslucency(void) +{ + int i; + int predefined_translucency[] = { + MT_FIRE, MT_SMOKE, MT_FATSHOT, MT_BRUISERSHOT, MT_SPAWNFIRE, + MT_TROOPSHOT, MT_HEADSHOT, MT_PLASMA, MT_BFG, MT_ARACHPLAZ, MT_PUFF, + MT_TFOG, MT_IFOG, MT_MISC12, MT_INV, MT_INS, MT_MEGA + }; + + for(i = 0; (size_t)i < sizeof(predefined_translucency)/sizeof(predefined_translucency[0]); i++) + { + if (!DEH_mobjinfo_bits[predefined_translucency[i]]) + { + if (default_comp[comp_translucency]) + mobjinfo[predefined_translucency[i]].flags &= ~MF_TRANSLUCENT; + else + mobjinfo[predefined_translucency[i]].flags |= MF_TRANSLUCENT; + } + } +} + +void deh_applyCompatibility(void) +{ + int comp_max = (compatibility_level == doom_12_compatibility ? 199 : 200); + + max_soul = (IsDehMaxSoul ? deh_max_soul : comp_max); + mega_health = (IsDehMegaHealth ? deh_mega_health : comp_max); + + if (comp[comp_maxhealth]) + { + maxhealth = 100; + maxhealthbonus = (IsDehMaxHealth ? deh_maxhealth : comp_max); + } + else + { + maxhealth = (IsDehMaxHealth ? deh_maxhealth : 100); + maxhealthbonus = maxhealth * 2; + } + + if (!DEH_mobjinfo_bits[MT_SKULL]) + { + if (compatibility_level == doom_12_compatibility) + mobjinfo[MT_SKULL].flags |= (MF_COUNTKILL); + else + mobjinfo[MT_SKULL].flags &= ~(MF_COUNTKILL); + } + + if (compatibility_level == doom_12_compatibility) + { + // Spiderdemon is not fullbright when attacking in versions before v1.4 + states[S_SPID_ATK1].frame &= ~FF_FULLBRIGHT; + states[S_SPID_ATK2].frame &= ~FF_FULLBRIGHT; + states[S_SPID_ATK3].frame &= ~FF_FULLBRIGHT; + states[S_SPID_ATK4].frame &= ~FF_FULLBRIGHT; + + // Powerups are not fullbright in v1.2 + // Soulsphere fullbright since v1.25s, the rest since v1.4 + states[S_SOUL].frame &= ~FF_FULLBRIGHT; + states[S_SOUL2].frame &= ~FF_FULLBRIGHT; + states[S_SOUL3].frame &= ~FF_FULLBRIGHT; + states[S_SOUL4].frame &= ~FF_FULLBRIGHT; + states[S_SOUL5].frame &= ~FF_FULLBRIGHT; + states[S_SOUL6].frame &= ~FF_FULLBRIGHT; + states[S_PINV].frame &= ~FF_FULLBRIGHT; + states[S_PINV2].frame &= ~FF_FULLBRIGHT; + states[S_PINV3].frame &= ~FF_FULLBRIGHT; + states[S_PINV4].frame &= ~FF_FULLBRIGHT; + states[S_PSTR].frame &= ~FF_FULLBRIGHT; + states[S_PINS].frame &= ~FF_FULLBRIGHT; + states[S_PINS2].frame &= ~FF_FULLBRIGHT; + states[S_PINS3].frame &= ~FF_FULLBRIGHT; + states[S_PINS4].frame &= ~FF_FULLBRIGHT; + states[S_SUIT].frame &= ~FF_FULLBRIGHT; + states[S_PMAP].frame &= ~FF_FULLBRIGHT; + states[S_PMAP2].frame &= ~FF_FULLBRIGHT; + states[S_PMAP3].frame &= ~FF_FULLBRIGHT; + states[S_PMAP4].frame &= ~FF_FULLBRIGHT; + states[S_PMAP5].frame &= ~FF_FULLBRIGHT; + states[S_PMAP6].frame &= ~FF_FULLBRIGHT; + } + + deh_changeCompTranslucency(); +} + +// ==================================================================== +// ProcessDehFile +// Purpose: Read and process a DEH or BEX file +// Args: filename -- name of the DEH/BEX file +// outfilename -- output file (DEHOUT.TXT), appended to here +// Returns: void +// +// killough 10/98: +// substantially modified to allow input from wad lumps instead of .deh files. + +void ProcessDehFile(const char *filename, const char *outfilename, int lumpnum) +{ + static FILE *fileout; // In case -dehout was used + DEHFILE infile, *filein = &infile; // killough 10/98 + char inbuffer[DEH_BUFFERMAX]; // Place to put the primary infostring + const char *file_or_lump; + static unsigned last_i; + static long filepos; + + // Open output file if we're writing output + if (outfilename && *outfilename && !fileout) + { + static dboolean firstfile = true; // to allow append to output log + if (!strcmp(outfilename, "-")) + fileout = stdout; + else + if (!(fileout=M_fopen(outfilename, firstfile ? "wt" : "at"))) + { + lprintf(LO_WARN, "Could not open -dehout file %s\n... using stdout.\n", + outfilename); + fileout = stdout; + } + firstfile = false; + } + + // killough 10/98: allow DEH files to come from wad lumps + + if (filename) + { + if (!(infile.f = M_fopen(filename,"rt"))) + { + lprintf(LO_WARN, "-deh file %s not found\n",filename); + return; // should be checked up front anyway + } + infile.lump = NULL; + file_or_lump = "file"; + } + else // DEH file comes from lump indicated by third argument + { + infile.size = W_LumpLength(lumpnum); + infile.inp = infile.lump = W_CacheLumpNum(lumpnum); + // [FG] skip empty DEHACKED lumps + if (!infile.inp) + { + lprintf(LO_WARN, "skipping empty DEHACKED (%d) lump\n",lumpnum); + return; + } + filename = lumpinfo[lumpnum].wadfile->name; + file_or_lump = "lump from"; + } + + lprintf(LO_INFO, "Loading DEH %s %s\n",file_or_lump,filename); + if (fileout) fprintf(fileout,"\nLoading DEH %s %s\n\n",file_or_lump,filename); + + // move deh_codeptr initialisation to D_BuildBEXTables + + // loop until end of file + + last_i = DEH_BLOCKMAX-1; + filepos = 0; + while (dehfgets(inbuffer,sizeof(inbuffer),filein)) + { + dboolean match; + unsigned i; + + lfstrip(inbuffer); + if (fileout) fprintf(fileout,"Line='%s'\n",inbuffer); + if (!*inbuffer || *inbuffer == '#' || *inbuffer == ' ') + continue; /* Blank line or comment line */ + + // -- If DEH_BLOCKMAX is set right, the processing is independently + // -- handled based on data in the deh_blocks[] structure array + + // killough 10/98: INCLUDE code rewritten to allow arbitrary nesting, + // and to greatly simplify code, fix memory leaks, other bugs + + if (!strnicmp(inbuffer,"INCLUDE",7)) // include a file + { + // preserve state while including a file + // killough 10/98: moved to here + + char *nextfile; + dboolean oldnotext = includenotext; // killough 10/98 + + // killough 10/98: exclude if inside wads (only to discourage + // the practice, since the code could otherwise handle it) + + if (infile.lump) + { + if (fileout) + fprintf(fileout, + "No files may be included from wads: %s\n",inbuffer); + continue; + } + + // check for no-text directive, used when including a DEH + // file but using the BEX format to handle strings + + if (!strnicmp(nextfile = ptr_lstrip(inbuffer+7),"NOTEXT",6)) + includenotext = true, nextfile = ptr_lstrip(nextfile+6); + + if (fileout) + fprintf(fileout,"Branching to include file %s...\n", nextfile); + + // killough 10/98: + // Second argument must be NULL to prevent closing fileout too soon + + ProcessDehFile(nextfile,NULL,0); // do the included file + + includenotext = oldnotext; + if (fileout) fprintf(fileout,"...continuing with %s\n",filename); + continue; + } + + for (match=0, i=0; i= 10 && last_i < DEH_BLOCKMAX-1) // restrict to BEX style lumps + { // process that same line again with the last valid block code handler + i = last_i; + dehfseek(filein, filepos); + } + + if (fileout) + fprintf(fileout,"Processing function [%d] for %s\n", + i, deh_blocks[i].key); + deh_blocks[i].fptr(filein,fileout,inbuffer); // call function + + filepos = dehftell(filein); // back up line start + } + + if (infile.lump) + W_UnlockLumpNum(lumpnum); // Mark purgable + else + fclose(infile.f); // Close real file + + if (outfilename) // killough 10/98: only at top recursion level + { + if (fileout != stdout) + fclose(fileout); + fileout = NULL; + } + + deh_applyCompatibility(); +} + +// ==================================================================== +// deh_procBexCodePointers +// Purpose: Handle [CODEPTR] block, BOOM Extension +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procBexCodePointers(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + int indexnum; + char mnemonic[DEH_MAXKEYLEN]; // to hold the codepointer mnemonic + int i; // looper + dboolean found; // know if we found this one during lookup or not + + // Ty 05/16/98 - initialize it to something, dummy! + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + + // for this one, we just read 'em until we hit a blank line + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98: really exit on blank line + + // killough 8/98: allow hex numbers in input: + if ( (3 != sscanf(inbuffer,"%s %i = %s", key, &indexnum, mnemonic)) + || (stricmp(key,"FRAME")) ) // NOTE: different format from normal + { + if (fpout) fprintf(fpout, + "Invalid BEX codepointer line - must start with 'FRAME': '%s'\n", + inbuffer); + return; // early return + } + + if (fpout) fprintf(fpout,"Processing pointer at index %d: %s\n", + indexnum, mnemonic); + if (indexnum < 0 || indexnum >= NUMSTATES) + { + if (fpout) fprintf(fpout,"Bad pointer number %d of %d\n", + indexnum, NUMSTATES); + return; // killough 10/98: fix SegViol + } + strcpy(key,"A_"); // reusing the key area to prefix the mnemonic + strcat(key,ptr_lstrip(mnemonic)); + + found = FALSE; + i= -1; // incremented to start at zero at the top of the loop + do // Ty 05/16/98 - fix loop logic to look for null ending entry + { + ++i; + if (!stricmp(key,deh_bexptrs[i].lookup)) + { // Ty 06/01/98 - add to states[].action for new djgcc version + states[indexnum].action = deh_bexptrs[i].cptr; // assign + if (fpout) fprintf(fpout, + " - applied %s from codeptr[%d] to states[%d]\n", + deh_bexptrs[i].lookup,i,indexnum); + found = TRUE; + } + } while (!found && (deh_bexptrs[i].cptr != NULL)); + + if (!found) + if (fpout) fprintf(fpout, + "Invalid frame pointer mnemonic '%s' at %d\n", + mnemonic, indexnum); + } + return; +} + +//--------------------------------------------------------------------------- +// To be on the safe, compatible side, we manually convert DEH bitflags +// to prboom types - POPE +//--------------------------------------------------------------------------- +static uint_64_t getConvertedDEHBits(uint_64_t bits) { + static const uint_64_t bitMap[32] = { + /* cf linuxdoom-1.10 p_mobj.h */ + MF_SPECIAL, // 0 Can be picked up - When touched the thing can be picked up. + MF_SOLID, // 1 Obstacle - The thing is solid and will not let you (or others) pass through it + MF_SHOOTABLE, // 2 Shootable - Can be shot. + MF_NOSECTOR, // 3 Total Invisibility - Invisible, but can be touched + MF_NOBLOCKMAP, // 4 Don't use the blocklinks (inert but displayable) + MF_AMBUSH, // 5 Semi deaf - The thing is a deaf monster + MF_JUSTHIT, // 6 In pain - Will try to attack right back after being hit + MF_JUSTATTACKED, // 7 Steps before attack - Will take at least one step before attacking + MF_SPAWNCEILING, // 8 Hangs from ceiling - When the level starts, this thing will be at ceiling height. + MF_NOGRAVITY, // 9 No gravity - Gravity does not affect this thing + MF_DROPOFF, // 10 Travels over cliffs - Monsters normally do not walk off ledges/steps they could not walk up. With this set they can walk off any height of cliff. Usually only used for flying monsters. + MF_PICKUP, // 11 Pick up items - The thing can pick up gettable items. + MF_NOCLIP, // 12 No clipping - Thing can walk through walls. + MF_SLIDE, // 13 Slides along walls - Keep info about sliding along walls (don't really know much about this one). + MF_FLOAT, // 14 Floating - Thing can move to any height + MF_TELEPORT, // 15 Semi no clipping - Don't cross lines or look at teleport heights. (don't really know much about this one either). + MF_MISSILE, // 16 Projectiles - Behaves like a projectile, explodes when hitting something that blocks movement + MF_DROPPED, // 17 Disappearing weapon - Dropped, not spawned (like an ammo clip) I have not had much success in using this one. + MF_SHADOW, // 18 Partial invisibility - Drawn like a spectre. + MF_NOBLOOD, // 19 Puffs (vs. bleeds) - If hit will spawn bullet puffs instead of blood splats. + MF_CORPSE, // 20 Sliding helpless - Will slide down steps when dead. + MF_INFLOAT, // 21 No auto levelling - float but not to target height (?) + MF_COUNTKILL, // 22 Affects kill % - counted as a killable enemy and affects percentage kills on level summary. + MF_COUNTITEM, // 23 Affects item % - affects percentage items gathered on level summary. + MF_SKULLFLY, // 24 Running - special handling for flying skulls. + MF_NOTDMATCH, // 25 Not in deathmatch - do not spawn in deathmatch (like keys) + MF_TRANSLATION1, // 26 Color 1 (grey / red) + MF_TRANSLATION2, // 27 Color 2 (brown / red) + // Convert bit 28 to MF_TOUCHY, not (MF_TRANSLATION1|MF_TRANSLATION2) + // fixes bug #1576151 (part 1) + MF_TOUCHY, // 28 - explodes on contact (MBF) + MF_BOUNCES, // 29 - bounces off walls and floors (MBF) + MF_FRIEND, // 30 - friendly monster helps players (MBF) + MF_TRANSLUCENT // e6y: Translucency via dehacked/bex doesn't work without it + }; + int i; + uint_64_t shiftBits = bits; + uint_64_t convertedBits = 0; + for (i=0; i<32; i++) { + if (shiftBits & 0x1) convertedBits |= bitMap[i]; + shiftBits >>= 1; + } + return convertedBits; +} + +//--------------------------------------------------------------------------- +// See usage below for an explanation of this function's existence - POPE +//--------------------------------------------------------------------------- +static void setMobjInfoValue(int mobjInfoIndex, int keyIndex, uint_64_t value) { + mobjinfo_t *mi; + if (mobjInfoIndex >= NUMMOBJTYPES || mobjInfoIndex < 0) return; + mi = &mobjinfo[mobjInfoIndex]; + switch (keyIndex) { + case 0: mi->doomednum = (int)value; return; + case 1: mi->spawnstate = (int)value; return; + case 2: mi->spawnhealth = (int)value; return; + case 3: mi->seestate = (int)value; return; + case 4: mi->seesound = (int)value; return; + case 5: mi->reactiontime = (int)value; return; + case 6: mi->attacksound = (int)value; return; + case 7: mi->painstate = (int)value; return; + case 8: mi->painchance = (int)value; return; + case 9: mi->painsound = (int)value; return; + case 10: mi->meleestate = (int)value; return; + case 11: mi->missilestate = (int)value; return; + case 12: mi->deathstate = (int)value; return; + case 13: mi->xdeathstate = (int)value; return; + case 14: mi->deathsound = (int)value; return; + case 15: mi->speed = (int)value; return; + case 16: mi->radius = (int)value; return; + case 17: mi->height = (int)value; return; + case 18: mi->mass = (int)value; return; + case 19: mi->damage = (int)value; return; + case 20: mi->activesound = (int)value; return; + case 21: mi->flags = value; return; + // e6y + // Correction of wrong processing of "Respawn frame" entry. + // There is no more synch on http://www.doomworld.com/sda/dwdemo/w303-115.zip + // (with correction in PIT_CheckThing) + case 22: + if (prboom_comp[PC_FORCE_INCORRECT_PROCESSING_OF_RESPAWN_FRAME_ENTRY].state) + { + mi->raisestate = (int)value; + return; + } + break; + case 23: + if (!prboom_comp[PC_FORCE_INCORRECT_PROCESSING_OF_RESPAWN_FRAME_ENTRY].state) + { + mi->raisestate = (int)value; + return; + } + break; + case 24: mi->droppeditem = (int)(value-1); return; // make it base zero (deh is 1-based) + case 25: mi->bloodcolor = (int)value; return; + default: return; + } +} + +// ==================================================================== +// deh_procThing +// Purpose: Handle DEH Thing block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +// Ty 8/27/98 - revised to also allow mnemonics for +// bit masks for monster attributes +// + +static void deh_procThing(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + int ix; + char *strval; + + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + if (fpout) fprintf(fpout,"Thing line: '%s'\n",inbuffer); + + // killough 8/98: allow hex numbers in input: + ix = sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout,"count=%d, Thing %d\n",ix, indexnum); + + // Note that the mobjinfo[] array is base zero, but object numbers + // in the dehacked file start with one. Grumble. + --indexnum; + + // now process the stuff + // Note that for Things we can look up the key and use its offset + // in the array of key strings as an int offset in the structure + + // get a line until a blank or end of file--it's not + // blank now because it has our incoming key in it + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + // e6y: Correction of wrong processing of Bits parameter if its value is equal to zero + // No more desync on HACX demos. + int bGetData; + + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); // toss the end of line + + // killough 11/98: really bail out on blank lines (break != continue) + if (!*inbuffer) break; // bail out with blank line between sections + + // e6y: Correction of wrong processing of Bits parameter if its value is equal to zero + // No more desync on HACX demos. + bGetData = deh_GetData(inbuffer,key,&value,&strval,fpout); + if (!bGetData) + // Old code: if (!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + for (ix=0; ix>32) & 0xffffffff, + (unsigned long)deh_mobjflags[iy].value & 0xffffffff, strval + ); + } + value |= deh_mobjflags[iy].value; + break; + } + if (iy >= DEH_MOBJFLAGMAX && fpout) { + fprintf(fpout, "Could not find bit mnemonic %s\n", strval); + } + } + + // Don't worry about conversion -- simply print values + if (fpout) { + fprintf(fpout, + "Bits = 0x%08lX%08lX\n", + (unsigned long)(value>>32) & 0xffffffff, + (unsigned long)value & 0xffffffff + ); + } + mobjinfo[indexnum].flags = value; // e6y + DEH_mobjinfo_bits[indexnum] = true; //e6y: changed by DEH + } + } + if (fpout) { + fprintf(fpout, + "Assigned 0x%08lx%08lx to %s(%d) at index %d\n", + (unsigned long)(value>>32) & 0xffffffff, + (unsigned long)value & 0xffffffff, key, indexnum, ix + ); + } + } + } + return; +} + +// ==================================================================== +// deh_procFrame +// Purpose: Handle DEH Frame block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procFrame(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout,"Processing Frame at index %d: %s\n",indexnum,key); + if (indexnum < 0 || indexnum >= NUMSTATES) + if (fpout) fprintf(fpout,"Bad frame number %d of %d\n",indexnum, NUMSTATES); + + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + if (!deh_strcasecmp(key,deh_state[0])) // Sprite number + { + if (fpout) fprintf(fpout," - sprite = %ld\n",(long)value); + states[indexnum].sprite = (spritenum_t)value; + } + else + if (!deh_strcasecmp(key,deh_state[1])) // Sprite subnumber + { + if (fpout) fprintf(fpout," - frame = %ld\n",(long)value); + states[indexnum].frame = (long)value; // long + } + else + if (!deh_strcasecmp(key,deh_state[2])) // Duration + { + if (fpout) fprintf(fpout," - tics = %ld\n",(long)value); + states[indexnum].tics = (long)value; // long + } + else + if (!deh_strcasecmp(key,deh_state[3])) // Next frame + { + if (fpout) fprintf(fpout," - nextstate = %ld\n",(long)value); + states[indexnum].nextstate = (statenum_t)value; + } + else + if (!deh_strcasecmp(key,deh_state[4])) // Codep frame (not set in Frame deh block) + { + if (fpout) fprintf(fpout," - codep, should not be set in Frame section!\n"); + /* nop */ ; + } + else + if (!deh_strcasecmp(key,deh_state[5])) // Unknown 1 + { + if (fpout) fprintf(fpout," - misc1 = %ld\n",(long)value); + states[indexnum].misc1 = (long)value; // long + } + else + if (!deh_strcasecmp(key,deh_state[6])) // Unknown 2 + { + if (fpout) fprintf(fpout," - misc2 = %ld\n",(long)value); + states[indexnum].misc2 = (long)value; // long + } + else + if (fpout) fprintf(fpout,"Invalid frame string index for '%s'\n",key); + } + return; +} + +// ==================================================================== +// deh_procPointer +// Purpose: Handle DEH Code pointer block, can use BEX [CODEPTR] instead +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procPointer(DEHFILE *fpin, FILE* fpout, char *line) // done +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + size_t i; // looper + + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + // NOTE: different format from normal + + // killough 8/98: allow hex numbers in input, fix error case: + if (sscanf(inbuffer,"%*s %*i (%s %i)",key, &indexnum) != 2) + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + return; + } + + if (fpout) fprintf(fpout,"Processing Pointer at index %d: %s\n",indexnum, key); + if (indexnum < 0 || indexnum >= NUMSTATES) + { + if (fpout) + fprintf(fpout,"Bad pointer number %d of %d\n",indexnum, NUMSTATES); + return; + } + + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + + if (value >= NUMSTATES) + { + if (fpout) + fprintf(fpout,"Bad pointer number %ld of %d\n",(long)value, NUMSTATES); + return; + } + + if (!deh_strcasecmp(key,deh_state[4])) // Codep frame (not set in Frame deh block) + { + states[indexnum].action = deh_codeptr[value]; + if (fpout) fprintf(fpout," - applied from codeptr[%ld] to states[%d]\n", + (long)value,indexnum); + // Write BEX-oriented line to match: + // for (i=0;i FRAME %d = %s\n", + indexnum, &deh_bexptrs[i].lookup[2]); + break; + } + if (deh_bexptrs[i].cptr == NULL) // stop at null entry + break; + } + } + else + if (fpout) fprintf(fpout,"Invalid frame pointer index for '%s' at %ld\n", + key, (long)value); + } + return; +} + +// ==================================================================== +// deh_procSounds +// Purpose: Handle DEH Sounds block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procSounds(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout,"Processing Sounds at index %d: %s\n", + indexnum, key); + if (indexnum < 0 || indexnum >= NUMSFX) + if (fpout) fprintf(fpout,"Bad sound number %d of %d\n", + indexnum, NUMSFX); + + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + if (!deh_strcasecmp(key,deh_sfxinfo[0])) // Offset + /* nop */ ; // we don't know what this is, I don't think + else + if (!deh_strcasecmp(key,deh_sfxinfo[1])) // Zero/One + S_sfx[indexnum].singularity = (int)value; + else + if (!deh_strcasecmp(key,deh_sfxinfo[2])) // Value + S_sfx[indexnum].priority = (int)value; + else + if (!deh_strcasecmp(key,deh_sfxinfo[3])) // Zero 1 + //S_sfx[indexnum].link = (sfxinfo_t *)value; + ; // .link - don't set pointers from DeHackEd + else + if (!deh_strcasecmp(key,deh_sfxinfo[4])) // Zero 2 + S_sfx[indexnum].pitch = (int)value; + else + if (!deh_strcasecmp(key,deh_sfxinfo[5])) // Zero 3 + S_sfx[indexnum].volume = (int)value; + else + if (!deh_strcasecmp(key,deh_sfxinfo[6])) // Zero 4 + //S_sfx[indexnum].data = (void *) value; // killough 5/3/98: changed cast + ; // .data - don't set pointers from DeHackEd + else + if (!deh_strcasecmp(key,deh_sfxinfo[7])) // Neg. One 1 + S_sfx[indexnum].usefulness = (int)value; + else + if (!deh_strcasecmp(key,deh_sfxinfo[8])) // Neg. One 2 + S_sfx[indexnum].lumpnum = (int)value; + else + if (fpout) fprintf(fpout, + "Invalid sound string index for '%s'\n",key); + } + return; +} + +// ==================================================================== +// deh_procAmmo +// Purpose: Handle DEH Ammo block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procAmmo(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout,"Processing Ammo at index %d: %s\n", + indexnum, key); + if (indexnum < 0 || indexnum >= NUMAMMO) + if (fpout) fprintf(fpout,"Bad ammo number %d of %d\n", + indexnum,NUMAMMO); + + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + if (!deh_strcasecmp(key,deh_ammo[0])) // Max ammo + maxammo[indexnum] = (int)value; + else + if (!deh_strcasecmp(key,deh_ammo[1])) // Per ammo + clipammo[indexnum] = (int)value; + else + if (fpout) fprintf(fpout,"Invalid ammo string index for '%s'\n",key); + } + return; +} + +// ==================================================================== +// deh_procWeapon +// Purpose: Handle DEH Weapon block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procWeapon(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout,"Processing Weapon at index %d: %s\n", + indexnum, key); + if (indexnum < 0 || indexnum >= NUMWEAPONS) + if (fpout) fprintf(fpout,"Bad weapon number %d of %d\n", + indexnum, NUMAMMO); + + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + if (!deh_strcasecmp(key,deh_weapon[0])) // Ammo type + weaponinfo[indexnum].ammo = (ammotype_t)value; + else + if (!deh_strcasecmp(key,deh_weapon[1])) // Deselect frame + weaponinfo[indexnum].upstate = (int)value; + else + if (!deh_strcasecmp(key,deh_weapon[2])) // Select frame + weaponinfo[indexnum].downstate = (int)value; + else + if (!deh_strcasecmp(key,deh_weapon[3])) // Bobbing frame + weaponinfo[indexnum].readystate = (int)value; + else + if (!deh_strcasecmp(key,deh_weapon[4])) // Shooting frame + weaponinfo[indexnum].atkstate = (int)value; + else + if (!deh_strcasecmp(key,deh_weapon[5])) // Firing frame + weaponinfo[indexnum].flashstate = (int)value; + else + if (fpout) fprintf(fpout,"Invalid weapon string index for '%s'\n",key); + } + return; +} + +// ==================================================================== +// deh_procSprite +// Purpose: Dummy - we do not support the DEH Sprite block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procSprite(DEHFILE *fpin, FILE* fpout, char *line) // Not supported +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + int indexnum; + + // Too little is known about what this is supposed to do, and + // there are better ways of handling sprite renaming. Not supported. + + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout, + "Ignoring Sprite offset change at index %d: %s\n",indexnum, key); + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + // ignore line + if (fpout) fprintf(fpout,"- %s\n",inbuffer); + } + return; +} + +// ==================================================================== +// deh_procPars +// Purpose: Handle BEX extension for PAR times +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procPars(DEHFILE *fpin, FILE* fpout, char *line) // extension +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + int indexnum; + int episode, level, partime, oldpar; + + // new item, par times + // usage: After [PARS] Par 0 section identifier, use one or more of these + // lines: + // par 3 5 120 + // par 14 230 + // The first would make the par for E3M5 be 120 seconds, and the + // second one makes the par for MAP14 be 230 seconds. The number + // of parameters on the line determines which group of par values + // is being changed. Error checking is done based on current fixed + // array sizes of[4][10] and [32] + + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout, + "Processing Par value at index %d: %s\n",indexnum, key); + // indexnum is a dummy entry + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(M_Strlwr(inbuffer)); // lowercase it + if (!*inbuffer) break; // killough 11/98 + if (3 != sscanf(inbuffer,"par %i %i %i",&episode, &level, &partime)) + { // not 3 + if (2 != sscanf(inbuffer,"par %i %i",&level, &partime)) + { // not 2 + if (fpout) fprintf(fpout,"Invalid par time setting string: %s\n",inbuffer); + } + else + { // is 2 + // Ty 07/11/98 - wrong range check, not zero-based + if (level < 1 || level > 32) // base 0 array (but 1-based parm) + { + if (fpout) fprintf(fpout,"Invalid MAPnn value MAP%d\n",level); + } + else + { + oldpar = cpars[level-1]; + if (fpout) fprintf(fpout,"Changed par time for MAP%02d from %d to %d\n",level,oldpar,partime); + cpars[level-1] = partime; + deh_pars = TRUE; + } + } + } + else + { // is 3 + // note that though it's a [4][10] array, the "left" and "top" aren't used, + // effectively making it a base 1 array. + // Ty 07/11/98 - level was being checked against max 3 - dumb error + // Note that episode 4 does not have par times per original design + // in Ultimate DOOM so that is not supported here. + if (episode < 1 || episode > 3 || level < 1 || level > 9) + { + if (fpout) fprintf(fpout, + "Invalid ExMx values E%dM%d\n",episode, level); + } + else + { + oldpar = pars[episode][level]; + pars[episode][level] = partime; + if (fpout) fprintf(fpout, + "Changed par time for E%dM%d from %d to %d\n", + episode,level,oldpar,partime); + deh_pars = TRUE; + } + } + } + return; +} + +// ==================================================================== +// deh_procCheat +// Purpose: Handle DEH Cheat block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procCheat(DEHFILE *fpin, FILE* fpout, char *line) // done +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + char ch = 0; // CPhipps - `writable' null string to initialise... + char *strval = &ch; // pointer to the value area + int ix, iy; // array indices + char *p; // utility pointer + + if (fpout) fprintf(fpout,"Processing Cheat: %s\n",line); + + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // Otherwise we got a (perhaps valid) cheat name, + // so look up the key in the array + + // killough 4/18/98: use main cheat code table in st_stuff.c now + for (ix=0; cheat[ix].cheat; ix++) + if (cheat[ix].deh_cheat) // killough 4/18/98: skip non-deh + { + if (!stricmp(key,cheat[ix].deh_cheat)) // found the cheat, ignored case + { + // replace it but don't overflow it. Use current length as limit. + // Ty 03/13/98 - add 0xff code + // Deal with the fact that the cheats in deh files are extended + // with character 0xFF to the original cheat length, which we don't do. + for (iy=0; strval[iy]; iy++) + strval[iy] = (strval[iy]==(char)0xff) ? '\0' : strval[iy]; + + iy = ix; // killough 4/18/98 + + // Ty 03/14/98 - skip leading spaces + p = strval; + while (*p == ' ') ++p; + // Ty 03/16/98 - change to use a strdup and orphan the original + // Also has the advantage of allowing length changes. + // strncpy(cheat[iy].cheat,p,strlen(cheat[iy].cheat)); +#if 0 + { // killough 9/12/98: disable cheats which are prefixes of this one + int i; + for (i=0; cheat[i].cheat; i++) + if (cheat[i].when & not_deh && + !strncasecmp(cheat[i].cheat, + cheat[iy].cheat, + strlen(cheat[i].cheat)) && i != iy) + cheat[i].deh_modified = true; + } +#endif + //e6y: ability to ignore cheats in dehacked files. + if (deh_apply_cheats && !M_CheckParm("-nocheats")) + { + cheat[iy].cheat = strdup(p); + if (fpout) fprintf(fpout, + "Assigned new cheat '%s' to cheat '%s'at index %d\n", + p, cheat[ix].deh_cheat, iy); // killough 4/18/98 + } + + } + } + if (fpout) fprintf(fpout,"- %s\n",inbuffer); + } + return; +} + +// ==================================================================== +// deh_procMisc +// Purpose: Handle DEH Misc block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procMisc(DEHFILE *fpin, FILE* fpout, char *line) // done +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // Otherwise it's ok + if (fpout) fprintf(fpout,"Processing Misc item '%s'\n", key); + + if (!deh_strcasecmp(key,deh_misc[0])) // Initial Health + initial_health = (int)value; + else + if (!deh_strcasecmp(key,deh_misc[1])) // Initial Bullets + initial_bullets = (int)value; + else + if (!deh_strcasecmp(key,deh_misc[2])) // Max Health + IsDehMaxHealth = true, deh_maxhealth = (int)value; //e6y + else + if (!deh_strcasecmp(key,deh_misc[3])) // Max Armor + max_armor = (int)value; + else + if (!deh_strcasecmp(key,deh_misc[4])) // Green Armor Class + green_armor_class = (int)value; + else + if (!deh_strcasecmp(key,deh_misc[5])) // Blue Armor Class + blue_armor_class = (int)value; + else + if (!deh_strcasecmp(key,deh_misc[6])) // Max Soulsphere + IsDehMaxSoul = true, deh_max_soul = (int)value; //e6y + else + if (!deh_strcasecmp(key,deh_misc[7])) // Soulsphere Health + soul_health = (int)value; + else + if (!deh_strcasecmp(key,deh_misc[8])) // Megasphere Health + IsDehMegaHealth = true, deh_mega_health = (int)value; //e6y + else + if (!deh_strcasecmp(key,deh_misc[9])) // God Mode Health + god_health = (int)value; + else + if (!deh_strcasecmp(key,deh_misc[10])) // IDFA Armor + idfa_armor = (int)value; + else + if (!deh_strcasecmp(key,deh_misc[11])) // IDFA Armor Class + idfa_armor_class = (int)value; + else + if (!deh_strcasecmp(key,deh_misc[12])) // IDKFA Armor + idkfa_armor = (int)value; + else + if (!deh_strcasecmp(key,deh_misc[13])) // IDKFA Armor Class + idkfa_armor_class = (int)value; + else + if (!deh_strcasecmp(key,deh_misc[14])) // BFG Cells/Shot + bfgcells = (int)value; + else + if (!deh_strcasecmp(key,deh_misc[15])) { // Monsters Infight + // e6y: Dehacked support - monsters infight + if (value == 202) monsters_infight = 0; + else if (value == 221) monsters_infight = 1; + else if (fpout) fprintf(fpout, + "Invalid value for 'Monsters Infight': %i", (int)value); + + /* No such switch in DOOM - nop */ //e6y ; + } else + if (fpout) fprintf(fpout, + "Invalid misc item string index for '%s'\n",key); + } + return; +} + +// ==================================================================== +// deh_procText +// Purpose: Handle DEH Text block +// Notes: We look things up in the current information and if found +// we replace it. At the same time we write the new and +// improved BEX syntax to the log file for future use. +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procText(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX*2]; // can't use line -- double size buffer too. + int i; // loop variable + int fromlen, tolen; // as specified on the text block line + int usedlen; // shorter of fromlen and tolen if not matched + dboolean found = FALSE; // to allow early exit once found + char* line2 = NULL; // duplicate line for rerouting + + // e6y + // Correction for DEHs which swap the values of two strings. For example: + // Text 4 4 Text 4 4; Text 6 6 Text 6 6 + // BOSSBOS2 BOS2BOSS; RUNNINSTALKS STALKSRUNNIN + // It corrects buggy behaviour on "All Hell is Breaking Loose" TC + // http://www.doomworld.com/idgames/index.php?id=6480 + static dboolean sprnames_state[NUMSPRITES+1]; + static dboolean S_sfx_state[NUMSFX]; + static dboolean S_music_state[NUMMUSIC]; + + // Ty 04/11/98 - Included file may have NOTEXT skip flag set + if (includenotext) // flag to skip included deh-style text + { + if (fpout) fprintf(fpout, + "Skipped text block because of notext directive\n"); + strcpy(inbuffer,line); + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + dehfgets(inbuffer, sizeof(inbuffer), fpin); // skip block + // Ty 05/17/98 - don't care if this fails + return; // ************** Early return + } + + // killough 8/98: allow hex numbers in input: + sscanf(line,"%s %i %i",key,&fromlen,&tolen); + if (fpout) fprintf(fpout, + "Processing Text (key=%s, from=%d, to=%d)\n", + key, fromlen, tolen); + + // killough 10/98: fix incorrect usage of feof + { + int c, totlen = 0; + while (totlen < fromlen + tolen && (c = dehfgetc(fpin)) != EOF) + if (c != '\r') + inbuffer[totlen++] = c; + inbuffer[totlen]='\0'; + } + + // if the from and to are 4, this may be a sprite rename. Check it + // against the array and process it as such if it matches. Remember + // that the original names are (and should remain) uppercase. + // Future: this will be from a separate [SPRITES] block. + if (fromlen==4 && tolen==4) + { + i=0; + while (sprnames[i]) // null terminated list in info.c //jff 3/19/98 + { //check pointer + if (!strnicmp(sprnames[i],inbuffer,fromlen) && !sprnames_state[i]) //not first char + { + if (fpout) fprintf(fpout, + "Changing name of sprite at index %d from %s to %*s\n", + i,sprnames[i],tolen,&inbuffer[fromlen]); + // Ty 03/18/98 - not using strdup because length is fixed + + // killough 10/98: but it's an array of pointers, so we must + // use strdup unless we redeclare sprnames and change all else + { + // CPhipps - fix constness problem + char *s; + sprnames[i] = s = strdup(sprnames[i]); + + //e6y: flag the sprite as changed + sprnames_state[i] = true; + + strncpy(s,&inbuffer[fromlen],tolen); + } + found = TRUE; + break; // only one will match--quit early + } + ++i; // next array element + } + } + + if (!found && fromlen < 7 && tolen < 7) // lengths of music and sfx are 6 or shorter + { + usedlen = (fromlen < tolen) ? fromlen : tolen; + if (fromlen != tolen) + if (fpout) fprintf(fpout, + "Warning: Mismatched lengths from=%d, to=%d, used %d\n", + fromlen, tolen, usedlen); + // Try sound effects entries - see sounds.c + for (i=1; i 12) ? "..." : "",fromlen,tolen); + if ((size_t)fromlen <= strlen(inbuffer)) + { + line2 = strdup(&inbuffer[fromlen]); + inbuffer[fromlen] = '\0'; + } + + deh_procStringSub(NULL, inbuffer, line2, fpout); + } + free(line2); // may be NULL, ignored by free() + return; +} + +static void deh_procError(DEHFILE *fpin, FILE* fpout, char *line) +{ + char inbuffer[DEH_BUFFERMAX]; + + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + if (fpout) fprintf(fpout,"Unmatched Block: '%s'\n",inbuffer); + return; +} + +// ==================================================================== +// deh_procStrings +// Purpose: Handle BEX [STRINGS] extension +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procStrings(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + char *strval; // holds the string value of the line + static size_t maxstrlen = 128; // maximum string length, bumped 128 at + // a time as needed + // holds the final result of the string after concatenation + static char *holdstring = NULL; + dboolean found = false; // looking for string continuation + + if (fpout) fprintf(fpout,"Processing extended string substitution\n"); + + if (!holdstring) holdstring = malloc(maxstrlen*sizeof(*holdstring)); + + *holdstring = '\0'; // empty string to start with + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + // Ty 04/24/98 - have to allow inbuffer to start with a blank for + // the continuations of C1TEXT etc. + while (!dehfeof(fpin) && *inbuffer) /* && (*inbuffer != ' ') */ + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + if (*inbuffer == '#') continue; // skip comment lines + lfstrip(inbuffer); + if (!*inbuffer && !*holdstring) break; // killough 11/98 + if (!*holdstring) // first one--get the key + { + if (!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + } + while (strlen(holdstring) + strlen(inbuffer) > maxstrlen) // Ty03/29/98 - fix stupid error + { + // killough 11/98: allocate enough the first time + maxstrlen = strlen(holdstring) + strlen(inbuffer); + if (fpout) fprintf(fpout, + "* increased buffer from to %ld for buffer size %d\n", + (long)maxstrlen,(int)strlen(inbuffer)); + holdstring = realloc(holdstring,maxstrlen*sizeof(*holdstring)); + } + // concatenate the whole buffer if continuation or the value iffirst + strcat(holdstring,ptr_lstrip(((*holdstring) ? inbuffer : strval))); + rstrip(holdstring); + // delete any trailing blanks past the backslash + // note that blanks before the backslash will be concatenated + // but ones at the beginning of the next line will not, allowing + // indentation in the file to read well without affecting the + // string itself. + if (holdstring[strlen(holdstring)-1] == '\\') + { + holdstring[strlen(holdstring)-1] = '\0'; + continue; // ready to concatenate + } + if (*holdstring) // didn't have a backslash, trap above would catch that + { + // go process the current string + found = deh_procStringSub(key, NULL, holdstring, fpout); // supply keyand not search string + + if (!found) + if (fpout) fprintf(fpout, + "Invalid string key '%s', substitution skipped.\n",key); + + *holdstring = '\0'; // empty string for the next one + } + } + return; +} + +// ==================================================================== +// deh_procStringSub +// Purpose: Common string parsing and handling routine for DEH and BEX +// Args: key -- place to put the mnemonic for the string if found +// lookfor -- original value string to look for +// newstring -- string to put in its place if found +// fpout -- file stream pointer for log file (DEHOUT.TXT) +// Returns: dboolean: True if string found, false if not +// +dboolean deh_procStringSub(char *key, char *lookfor, char *newstring, FILE *fpout) +{ + dboolean found; // loop exit flag + int i; // looper + + found = false; + for (i=0;i '%s'\n",key,newstring); + + if (!key) + if (fpout) fprintf(fpout, + "Assigned '%.12s%s' to'%.12s%s' at key %s\n", + lookfor, (strlen(lookfor) > 12) ? "..." : "", + newstring, (strlen(newstring) > 12) ? "..." :"", + deh_strlookup[i].lookup); + + if (!key) // must have passed an old style string so showBEX + if (fpout) fprintf(fpout, + "*BEX FORMAT:\n%s = %s\n*END BEX\n", + deh_strlookup[i].lookup, + dehReformatStr(newstring)); + + break; + } + } + if (!found) + if (fpout) fprintf(fpout, + "Could not find '%.12s'\n",key ? key: lookfor); + + return found; +} + +//======================================================================== +// haleyjd 9/22/99 +// +// deh_procHelperThing +// +// Allows handy substitution of any thing for helper dogs. DEH patches +// are being made frequently for this purpose and it requires a complete +// rewiring of the DOG thing. I feel this is a waste of effort, and so +// have added this new [HELPER] BEX block + +static void deh_procHelperThing(DEHFILE *fpin, FILE *fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // Otherwise it's ok + if (fpout) + { + fprintf(fpout,"Processing Helper Thing item '%s'\n", key); + fprintf(fpout,"value is %i", (int)value); + } + if (!strncasecmp(key, "type", 4)) + HelperThing = (int)value; + } + return; +} + +// +// deh_procBexSprites +// +// Supports sprite name substitutions without requiring use +// of the DeHackEd Text block +// +static void deh_procBexSprites(DEHFILE *fpin, FILE *fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + char *strval; // holds the string value of the line + char candidate[5]; + int rover; + + if(fpout) + fprintf(fpout,"Processing sprite name substitution\n"); + + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + + while(!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if(!dehfgets(inbuffer, sizeof(inbuffer), fpin)) + break; + if(*inbuffer == '#') + continue; // skip comment lines + lfstrip(inbuffer); + if(!*inbuffer) + break; // killough 11/98 + if(!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if(fpout) + fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // do it + memset(candidate, 0, sizeof(candidate)); + strncpy(candidate, ptr_lstrip(strval), 4); + if(strlen(candidate) != 4) + { + if(fpout) + fprintf(fpout, "Bad length for sprite name '%s'\n", + candidate); + continue; + } + + rover = 0; + while(deh_spritenames[rover]) + { + if(!strncasecmp(deh_spritenames[rover], key, 4)) + { + if(fpout) + fprintf(fpout, "Substituting '%s' for sprite '%s'\n", + candidate, deh_spritenames[rover]); + + sprnames[rover] = strdup(candidate); + break; + } + rover++; + } + } +} + +// ditto for sound names +static void deh_procBexSounds(DEHFILE *fpin, FILE *fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + char *strval; // holds the string value of the line + char candidate[7]; + int rover; + size_t len; + + if(fpout) + fprintf(fpout,"Processing sound name substitution\n"); + + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + + while(!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if(!dehfgets(inbuffer, sizeof(inbuffer), fpin)) + break; + if(*inbuffer == '#') + continue; // skip comment lines + lfstrip(inbuffer); + if(!*inbuffer) + break; // killough 11/98 + if(!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if(fpout) + fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // do it + memset(candidate, 0, 7); + strncpy(candidate, ptr_lstrip(strval), 6); + len = strlen(candidate); + if(len < 1 || len > 6) + { + if(fpout) + fprintf(fpout, "Bad length for sound name '%s'\n", + candidate); + continue; + } + + rover = 1; + while(deh_soundnames[rover]) + { + if(!strncasecmp(deh_soundnames[rover], key, 6)) + { + if(fpout) + fprintf(fpout, "Substituting '%s' for sound '%s'\n", + candidate, deh_soundnames[rover]); + + S_sfx[rover].name = strdup(candidate); + break; + } + rover++; + } + } +} + +// ditto for music names +static void deh_procBexMusic(DEHFILE *fpin, FILE *fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + char *strval; // holds the string value of the line + char candidate[7]; + int rover; + size_t len; + + if(fpout) + fprintf(fpout,"Processing music name substitution\n"); + + strncpy(inbuffer,line,DEH_BUFFERMAX-1); + + while(!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if(!dehfgets(inbuffer, sizeof(inbuffer), fpin)) + break; + if(*inbuffer == '#') + continue; // skip comment lines + lfstrip(inbuffer); + if(!*inbuffer) + break; // killough 11/98 + if(!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if(fpout) + fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // do it + memset(candidate, 0, 7); + strncpy(candidate, ptr_lstrip(strval), 6); + len = strlen(candidate); + if(len < 1 || len > 6) + { + if(fpout) + fprintf(fpout, "Bad length for music name '%s'\n", + candidate); + continue; + } + + rover = 1; + while(deh_musicnames[rover]) + { + if(!strncasecmp(deh_musicnames[rover], key, 6)) + { + if(fpout) + fprintf(fpout, "Substituting '%s' for music '%s'\n", + candidate, deh_musicnames[rover]); + + S_music[rover].name = strdup(candidate); + break; + } + rover++; + } + } +} + +// ==================================================================== +// General utility function(s) +// ==================================================================== + +// ==================================================================== +// dehReformatStr +// Purpose: Convert a string into a continuous string with embedded +// linefeeds for "\n" sequences in the source string +// Args: string -- the string to convert +// Returns: the converted string (converted in a static buffer) +// +char *dehReformatStr(char *string) +{ + static char buff[DEH_BUFFERMAX]; // only processing the changed string, + // don't need double buffer + char *s, *t; + + s = string; // source + t = buff; // target + // let's play... + + while (*s) + { + if (*s == '\n') + ++s, *t++ = '\\', *t++ = 'n', *t++ = '\\', *t++='\n'; + else + *t++ = *s++; + } + *t = '\0'; + return buff; +} + +// ==================================================================== +// lfstrip +// Purpose: Strips CR/LF off the end of a string +// Args: s -- the string to work on +// Returns: void -- the string is modified in place +// +// killough 10/98: only strip at end of line, not entire string + +void lfstrip(char *s) // strip the \r and/or \n off of a line +{ + char *p = s+strlen(s); + while (p > s && (*--p=='\r' || *p=='\n')) + *p = 0; +} + +// ==================================================================== +// rstrip +// Purpose: Strips trailing blanks off a string +// Args: s -- the string to work on +// Returns: void -- the string is modified in place +// +void rstrip(char *s) // strip trailing whitespace +{ + char *p = s+strlen(s); // killough 4/4/98: same here + while (p > s && isspace(*--p)) // break on first non-whitespace + *p='\0'; +} + +// ==================================================================== +// ptr_lstrip +// Purpose: Points past leading whitespace in a string +// Args: s -- the string to work on +// Returns: char * pointing to the first nonblank character in the +// string. The original string is not changed. +// +char *ptr_lstrip(char *p) // point past leading whitespace +{ + while (isspace(*p)) + p++; + return p; +} + +// ==================================================================== +// deh_GetData +// Purpose: Get a key and data pair from a passed string +// Args: s -- the string to be examined +// k -- a place to put the key +// l -- pointer to a long integer to store the number +// strval -- a pointer to the place in s where the number +// value comes from. Pass NULL to not use this. +// fpout -- stream pointer to output log (DEHOUT.TXT) +// Notes: Expects a key phrase, optional space, equal sign, +// optional space and a value, mostly an int but treated +// as a long just in case. The passed pointer to hold +// the key must be DEH_MAXKEYLEN in size. + +dboolean deh_GetData(char *s, char *k, uint_64_t *l, char **strval, FILE *fpout) +{ + char *t; // current char + int val; // to hold value of pair + char buffer[DEH_MAXKEYLEN]; // to hold key in progress + // e6y: Correction of wrong processing of Bits parameter if its value is equal to zero + // No more desync on HACX demos. + dboolean okrc = 1; // assume good unless we have problems + int i; // iterator + + *buffer = '\0'; + val = 0; // defaults in case not otherwise set + for (i=0, t=s; *t && i < DEH_MAXKEYLEN; t++, i++) + { + if (*t == '=') break; + buffer[i] = *t; // copy it + } + buffer[--i] = '\0'; // terminate the key before the '=' + if (!*t) // end of string with no equal sign + { + okrc = FALSE; + } + else + { + if (!*++t) + { + val = 0; // in case "thiskey =" with no value + okrc = FALSE; + } + // we've incremented t + // e6y: Correction of wrong processing of Bits parameter if its value is equal to zero + // No more desync on HACX demos. + // Old code: e6y val = strtol(t,NULL,0); // killough 8/9/98: allow hex or octal input + if (!M_StrToInt(t,&val)) + { + val = 0; + okrc = 2; + } + } + + // go put the results in the passed pointers + *l = val; // may be a faked zero + + // if spaces between key and equal sign, strip them + strcpy(k,ptr_lstrip(buffer)); // could be a zero-length string + + if (strval != NULL) // pass NULL if you don't want this back + *strval = t; // pointer, has to be somewhere in s, + // even if pointing at the zero byte. + + return(okrc); +} diff --git a/src/d_deh.h b/src/d_deh.h new file mode 100644 index 0000000..4ae1ccd --- /dev/null +++ b/src/d_deh.h @@ -0,0 +1,1126 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2006 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Dehacked file support + * New for the TeamTNT "Boom" engine + * + * Author: Ty Halderman, TeamTNT + * + * Description: This file translates the #defined string constants + * to named variables to externalize them for deh/bex changes. + * Should be able to compile with D_FRENCH (for example) and still + * work (untested). + * + */ + +#ifndef __D_DEH__ +#define __D_DEH__ + +extern int deh_apply_cheats; + +void ProcessDehFile(const char *filename, const char *outfilename, int lumpnum); + +// +// Ty 03/22/98 - note that we are keeping the english versions and +// comments in this file +// New string names all start with an extra s_ to avoid conflicts, +// but are otherwise identical to the original including uppercase. +// This is partly to keep the changes simple and partly for easier +// identification of the locations in which they're used. +// +// Printed strings for translation +// + +// +// D_Main.C +// +//#define D_DEVSTR "Development mode ON.\n" +extern const char *s_D_DEVSTR; // = D_DEVSTR; +//#define D_CDROM "CD-ROM Version: default.cfg from c:\\doomdata\n" +extern const char *s_D_CDROM; // = D_CDROM; + +// +// M_Menu.C +// +//#define PRESSKEY "press a key." +extern const char *s_PRESSKEY; // = PRESSKEY; +//#define PRESSYN "press y or n." +extern const char *s_PRESSYN; // = PRESSYN; +//#define QUITMSG "are you sure you want to\nquit this great game?" +extern const char *s_QUITMSG; // = QUITMSG; +//#define LOADNET "you can't do load while in a net game!\n\n"PRESSKEY +extern const char *s_LOADNET; // = LOADNET; +//#define QLOADNET "you can't quickload during a netgame!\n\n"PRESSKEY +extern const char *s_QLOADNET; // = QLOADNET; +//#define QSAVESPOT "you haven't picked a quicksave slot yet!\n\n"PRESSKEY +extern const char *s_QSAVESPOT; // = QSAVESPOT; +//#define SAVEDEAD "you can't save if you aren't playing!\n\n"PRESSKEY +extern const char *s_SAVEDEAD; // = SAVEDEAD; +//#define QSPROMPT "quicksave over your game named\n\n'%s'?\n\n"PRESSYN +extern const char *s_QSPROMPT; // = QSPROMPT; +//#define QLPROMPT "do you want to quickload the game named\n\n'%s'?\n\n"PRESSYN +extern const char *s_QLPROMPT; // = QLPROMPT; + +/* +#define NEWGAME \ +"you can't start a new game\n"\ +"while in a network game.\n\n"PRESSKEY +*/ +extern const char *s_NEWGAME; // = NEWGAME; + +// CPhipps - message given when asked if to restart the level +extern const char *s_RESTARTLEVEL; + +/* +#define NIGHTMARE \ +"are you sure? this skill level\n"\ +"isn't even remotely fair.\n\n"PRESSYN +*/ +extern const char *s_NIGHTMARE; // = NIGHTMARE; + +/* +#define SWSTRING \ +"this is the shareware version of doom.\n\n"\ +"you need to order the entire trilogy.\n\n"PRESSKEY +*/ +extern const char *s_SWSTRING; // = SWSTRING; + +//#define MSGOFF "Messages OFF" +extern const char *s_MSGOFF; // = MSGOFF; +//#define MSGON "Messages ON" +extern const char *s_MSGON; // = MSGON; +//#define NETEND "you can't end a netgame!\n\n"PRESSKEY +extern const char *s_NETEND; // = NETEND; +//#define ENDGAME "are you sure you want to end the game?\n\n"PRESSYN +extern const char *s_ENDGAME; // = ENDGAME; + +//#define DOSY "(press y to quit)" +extern const char *s_DOSY; // = DOSY; + +//#define DETAILHI "High detail" +extern const char *s_DETAILHI; // = DETAILHI; +//#define DETAILLO "Low detail" +extern const char *s_DETAILLO; // = DETAILLO; +//#define GAMMALVL0 "Gamma correction OFF" +extern const char *s_GAMMALVL0; // = GAMMALVL0; +//#define GAMMALVL1 "Gamma correction level 1" +extern const char *s_GAMMALVL1; // = GAMMALVL1; +//#define GAMMALVL2 "Gamma correction level 2" +extern const char *s_GAMMALVL2; // = GAMMALVL2; +//#define GAMMALVL3 "Gamma correction level 3" +extern const char *s_GAMMALVL3; // = GAMMALVL3; +//#define GAMMALVL4 "Gamma correction level 4" +extern const char *s_GAMMALVL4; // = GAMMALVL4; +//#define EMPTYSTRING "empty slot" +extern const char *s_EMPTYSTRING; // = EMPTYSTRING; + +// +// P_inter.C +// +//#define GOTARMOR "Picked up the armor." +extern const char *s_GOTARMOR; // = GOTARMOR; +//#define GOTMEGA "Picked up the MegaArmor!" +extern const char *s_GOTMEGA; // = GOTMEGA; +//#define GOTHTHBONUS "Picked up a health bonus." +extern const char *s_GOTHTHBONUS; // = GOTHTHBONUS; +//#define GOTARMBONUS "Picked up an armor bonus." +extern const char *s_GOTARMBONUS; // = GOTARMBONUS; +//#define GOTSTIM "Picked up a stimpack." +extern const char *s_GOTSTIM; // = GOTSTIM; +//#define GOTMEDINEED "Picked up a medikit that you REALLY need!" +extern const char *s_GOTMEDINEED; // = GOTMEDINEED; +//#define GOTMEDIKIT "Picked up a medikit." +extern const char *s_GOTMEDIKIT; // = GOTMEDIKIT; +//#define GOTSUPER "Supercharge!" +extern const char *s_GOTSUPER; // = GOTSUPER; + +//#define GOTBLUECARD "Picked up a blue keycard." +extern const char *s_GOTBLUECARD; // = GOTBLUECARD; +//#define GOTYELWCARD "Picked up a yellow keycard." +extern const char *s_GOTYELWCARD; // = GOTYELWCARD; +//#define GOTREDCARD "Picked up a red keycard." +extern const char *s_GOTREDCARD; // = GOTREDCARD; +//#define GOTBLUESKUL "Picked up a blue skull key." +extern const char *s_GOTBLUESKUL; // = GOTBLUESKUL; +//#define GOTYELWSKUL "Picked up a yellow skull key." +extern const char *s_GOTYELWSKUL; // = GOTYELWSKUL; +//#define GOTREDSKULL "Picked up a red skull key." +extern const char *s_GOTREDSKULL; // = GOTREDSKULL; + +//#define GOTINVUL "Invulnerability!" +extern const char *s_GOTINVUL; // = GOTINVUL; +//#define GOTBERSERK "Berserk!" +extern const char *s_GOTBERSERK; // = GOTBERSERK; +//#define GOTINVIS "Partial Invisibility" +extern const char *s_GOTINVIS; // = GOTINVIS; +//#define GOTSUIT "Radiation Shielding Suit" +extern const char *s_GOTSUIT; // = GOTSUIT; +//#define GOTMAP "Computer Area Map" +extern const char *s_GOTMAP; // = GOTMAP; +//#define GOTVISOR "Light Amplification Visor" +extern const char *s_GOTVISOR; // = GOTVISOR; +//#define GOTMSPHERE "MegaSphere!" +extern const char *s_GOTMSPHERE; // = GOTMSPHERE; + +//#define GOTCLIP "Picked up a clip." +extern const char *s_GOTCLIP; // = GOTCLIP; +//#define GOTCLIPBOX "Picked up a box of bullets." +extern const char *s_GOTCLIPBOX; // = GOTCLIPBOX; +//#define GOTROCKET "Picked up a rocket." +extern const char *s_GOTROCKET; // = GOTROCKET; +//#define GOTROCKBOX "Picked up a box of rockets." +extern const char *s_GOTROCKBOX; // = GOTROCKBOX; +//#define GOTCELL "Picked up an energy cell." +extern const char *s_GOTCELL; // = GOTCELL; +//#define GOTCELLBOX "Picked up an energy cell pack." +extern const char *s_GOTCELLBOX; // = GOTCELLBOX; +//#define GOTSHELLS "Picked up 4 shotgun shells." +extern const char *s_GOTSHELLS; // = GOTSHELLS; +//#define GOTSHELLBOX "Picked up a box of shotgun shells." +extern const char *s_GOTSHELLBOX; // = GOTSHELLBOX; +//#define GOTBACKPACK "Picked up a backpack full of ammo!" +extern const char *s_GOTBACKPACK; // = GOTBACKPACK; + +//#define GOTBFG9000 "You got the BFG9000! Oh, yes." +extern const char *s_GOTBFG9000; // = GOTBFG9000; +//#define GOTCHAINGUN "You got the chaingun!" +extern const char *s_GOTCHAINGUN; // = GOTCHAINGUN; +//#define GOTCHAINSAW "A chainsaw! Find some meat!" +extern const char *s_GOTCHAINSAW; // = GOTCHAINSAW; +//#define GOTLAUNCHER "You got the rocket launcher!" +extern const char *s_GOTLAUNCHER; // = GOTLAUNCHER; +//#define GOTPLASMA "You got the plasma gun!" +extern const char *s_GOTPLASMA; // = GOTPLASMA; +//#define GOTSHOTGUN "You got the shotgun!" +extern const char *s_GOTSHOTGUN; // = GOTSHOTGUN; +//#define GOTSHOTGUN2 "You got the super shotgun!" +extern const char *s_GOTSHOTGUN2; // = GOTSHOTGUN2; + +// +// P_Doors.C +// +//#define PD_BLUEO "You need a blue key to activate this object" +extern const char *s_PD_BLUEO; // = PD_BLUEO; +//#define PD_REDO "You need a red key to activate this object" +extern const char *s_PD_REDO; // = PD_REDO; +//#define PD_YELLOWO "You need a yellow key to activate this object" +extern const char *s_PD_YELLOWO; // = PD_YELLOWO; +//#define PD_BLUEK "You need a blue key to open this door" +extern const char *s_PD_BLUEK; // = PD_BLUEK; +//#define PD_REDK "You need a red key to open this door" +extern const char *s_PD_REDK; // = PD_REDK; +//#define PD_YELLOWK "You need a yellow key to open this door" +extern const char *s_PD_YELLOWK; // = PD_YELLOWK; +//jff 02/05/98 Create messages specific to card and skull keys +//#define PD_BLUEC "You need a blue card to open this door" +extern const char *s_PD_BLUEC; // = PD_BLUEC; +//#define PD_REDC "You need a red card to open this door" +extern const char *s_PD_REDC; // = PD_REDC; +//#define PD_YELLOWC "You need a yellow card to open this door" +extern const char *s_PD_YELLOWC; // = PD_YELLOWC; +//#define PD_BLUES "You need a blue skull to open this door" +extern const char *s_PD_BLUES; // = PD_BLUES; +//#define PD_REDS "You need a red skull to open this door" +extern const char *s_PD_REDS; // = PD_REDS; +//#define PD_YELLOWS "You need a yellow skull to open this door" +extern const char *s_PD_YELLOWS; // = PD_YELLOWS; +//#define PD_ANY "Any key will open this door" +extern const char *s_PD_ANY; // = PD_ANY; +//#define PD_ALL3 "You need all three keys to open this door" +extern const char *s_PD_ALL3; // = PD_ALL3; +//#define PD_ALL6 "You need all six keys to open this door" +extern const char *s_PD_ALL6; // = PD_ALL6; + +// +// G_game.C +// +//#define GGSAVED "game saved." +extern const char *s_GGSAVED; // = GGSAVED; + +// +// HU_stuff.C +// +//#define HUSTR_MSGU "[Message unsent]" +extern const char *s_HUSTR_MSGU; // = HUSTR_MSGU; + +//#define HUSTR_E1M1 "E1M1: Hangar" +extern const char *s_HUSTR_E1M1; // = HUSTR_E1M1; +//#define HUSTR_E1M2 "E1M2: Nuclear Plant" +extern const char *s_HUSTR_E1M2; // = HUSTR_E1M2; +//#define HUSTR_E1M3 "E1M3: Toxin Refinery" +extern const char *s_HUSTR_E1M3; // = HUSTR_E1M3; +//#define HUSTR_E1M4 "E1M4: Command Control" +extern const char *s_HUSTR_E1M4; // = HUSTR_E1M4; +//#define HUSTR_E1M5 "E1M5: Phobos Lab" +extern const char *s_HUSTR_E1M5; // = HUSTR_E1M5; +//#define HUSTR_E1M6 "E1M6: Central Processing" +extern const char *s_HUSTR_E1M6; // = HUSTR_E1M6; +//#define HUSTR_E1M7 "E1M7: Computer Station" +extern const char *s_HUSTR_E1M7; // = HUSTR_E1M7; +//#define HUSTR_E1M8 "E1M8: Phobos Anomaly" +extern const char *s_HUSTR_E1M8; // = HUSTR_E1M8; +//#define HUSTR_E1M9 "E1M9: Military Base" +extern const char *s_HUSTR_E1M9; // = HUSTR_E1M9; + +//#define HUSTR_E2M1 "E2M1: Deimos Anomaly" +extern const char *s_HUSTR_E2M1; // = HUSTR_E2M1; +//#define HUSTR_E2M2 "E2M2: Containment Area" +extern const char *s_HUSTR_E2M2; // = HUSTR_E2M2; +//#define HUSTR_E2M3 "E2M3: Refinery" +extern const char *s_HUSTR_E2M3; // = HUSTR_E2M3; +//#define HUSTR_E2M4 "E2M4: Deimos Lab" +extern const char *s_HUSTR_E2M4; // = HUSTR_E2M4; +//#define HUSTR_E2M5 "E2M5: Command Center" +extern const char *s_HUSTR_E2M5; // = HUSTR_E2M5; +//#define HUSTR_E2M6 "E2M6: Halls of the Damned" +extern const char *s_HUSTR_E2M6; // = HUSTR_E2M6; +//#define HUSTR_E2M7 "E2M7: Spawning Vats" +extern const char *s_HUSTR_E2M7; // = HUSTR_E2M7; +//#define HUSTR_E2M8 "E2M8: Tower of Babel" +extern const char *s_HUSTR_E2M8; // = HUSTR_E2M8; +//#define HUSTR_E2M9 "E2M9: Fortress of Mystery" +extern const char *s_HUSTR_E2M9; // = HUSTR_E2M9; + +//#define HUSTR_E3M1 "E3M1: Hell Keep" +extern const char *s_HUSTR_E3M1; // = HUSTR_E3M1; +//#define HUSTR_E3M2 "E3M2: Slough of Despair" +extern const char *s_HUSTR_E3M2; // = HUSTR_E3M2; +//#define HUSTR_E3M3 "E3M3: Pandemonium" +extern const char *s_HUSTR_E3M3; // = HUSTR_E3M3; +//#define HUSTR_E3M4 "E3M4: House of Pain" +extern const char *s_HUSTR_E3M4; // = HUSTR_E3M4; +//#define HUSTR_E3M5 "E3M5: Unholy Cathedral" +extern const char *s_HUSTR_E3M5; // = HUSTR_E3M5; +//#define HUSTR_E3M6 "E3M6: Mt. Erebus" +extern const char *s_HUSTR_E3M6; // = HUSTR_E3M6; +//#define HUSTR_E3M7 "E3M7: Limbo" +extern const char *s_HUSTR_E3M7; // = HUSTR_E3M7; +//#define HUSTR_E3M8 "E3M8: Dis" +extern const char *s_HUSTR_E3M8; // = HUSTR_E3M8; +//#define HUSTR_E3M9 "E3M9: Warrens" +extern const char *s_HUSTR_E3M9; // = HUSTR_E3M9; + +//#define HUSTR_E4M1 "E4M1: Hell Beneath" +extern const char *s_HUSTR_E4M1; // = HUSTR_E4M1; +//#define HUSTR_E4M2 "E4M2: Perfect Hatred" +extern const char *s_HUSTR_E4M2; // = HUSTR_E4M2; +//#define HUSTR_E4M3 "E4M3: Sever The Wicked" +extern const char *s_HUSTR_E4M3; // = HUSTR_E4M3; +//#define HUSTR_E4M4 "E4M4: Unruly Evil" +extern const char *s_HUSTR_E4M4; // = HUSTR_E4M4; +//#define HUSTR_E4M5 "E4M5: They Will Repent" +extern const char *s_HUSTR_E4M5; // = HUSTR_E4M5; +//#define HUSTR_E4M6 "E4M6: Against Thee Wickedly" +extern const char *s_HUSTR_E4M6; // = HUSTR_E4M6; +//#define HUSTR_E4M7 "E4M7: And Hell Followed" +extern const char *s_HUSTR_E4M7; // = HUSTR_E4M7; +//#define HUSTR_E4M8 "E4M8: Unto The Cruel" +extern const char *s_HUSTR_E4M8; // = HUSTR_E4M8; +//#define HUSTR_E4M9 "E4M9: Fear" +extern const char *s_HUSTR_E4M9; // = HUSTR_E4M9; + +//#define HUSTR_1 "level 1: entryway" +extern const char *s_HUSTR_1; // = HUSTR_1; +//#define HUSTR_2 "level 2: underhalls" +extern const char *s_HUSTR_2; // = HUSTR_2; +//#define HUSTR_3 "level 3: the gantlet" +extern const char *s_HUSTR_3; // = HUSTR_3; +//#define HUSTR_4 "level 4: the focus" +extern const char *s_HUSTR_4; // = HUSTR_4; +//#define HUSTR_5 "level 5: the waste tunnels" +extern const char *s_HUSTR_5; // = HUSTR_5; +//#define HUSTR_6 "level 6: the crusher" +extern const char *s_HUSTR_6; // = HUSTR_6; +//#define HUSTR_7 "level 7: dead simple" +extern const char *s_HUSTR_7; // = HUSTR_7; +//#define HUSTR_8 "level 8: tricks and traps" +extern const char *s_HUSTR_8; // = HUSTR_8; +//#define HUSTR_9 "level 9: the pit" +extern const char *s_HUSTR_9; // = HUSTR_9; +//#define HUSTR_10 "level 10: refueling base" +extern const char *s_HUSTR_10; // = HUSTR_10; +//#define HUSTR_11 "level 11: 'o' of destruction!" +extern const char *s_HUSTR_11; // = HUSTR_11; + +//#define HUSTR_12 "level 12: the factory" +extern const char *s_HUSTR_12; // = HUSTR_12; +//#define HUSTR_13 "level 13: downtown" +extern const char *s_HUSTR_13; // = HUSTR_13; +//#define HUSTR_14 "level 14: the inmost dens" +extern const char *s_HUSTR_14; // = HUSTR_14; +//#define HUSTR_15 "level 15: industrial zone" +extern const char *s_HUSTR_15; // = HUSTR_15; +//#define HUSTR_16 "level 16: suburbs" +extern const char *s_HUSTR_16; // = HUSTR_16; +//#define HUSTR_17 "level 17: tenements" +extern const char *s_HUSTR_17; // = HUSTR_17; +//#define HUSTR_18 "level 18: the courtyard" +extern const char *s_HUSTR_18; // = HUSTR_18; +//#define HUSTR_19 "level 19: the citadel" +extern const char *s_HUSTR_19; // = HUSTR_19; +//#define HUSTR_20 "level 20: gotcha!" +extern const char *s_HUSTR_20; // = HUSTR_20; + +//#define HUSTR_21 "level 21: nirvana" +extern const char *s_HUSTR_21; // = HUSTR_21; +//#define HUSTR_22 "level 22: the catacombs" +extern const char *s_HUSTR_22; // = HUSTR_22; +//#define HUSTR_23 "level 23: barrels o' fun" +extern const char *s_HUSTR_23; // = HUSTR_23; +//#define HUSTR_24 "level 24: the chasm" +extern const char *s_HUSTR_24; // = HUSTR_24; +//#define HUSTR_25 "level 25: bloodfalls" +extern const char *s_HUSTR_25; // = HUSTR_25; +//#define HUSTR_26 "level 26: the abandoned mines" +extern const char *s_HUSTR_26; // = HUSTR_26; +//#define HUSTR_27 "level 27: monster condo" +extern const char *s_HUSTR_27; // = HUSTR_27; +//#define HUSTR_28 "level 28: the spirit world" +extern const char *s_HUSTR_28; // = HUSTR_28; +//#define HUSTR_29 "level 29: the living end" +extern const char *s_HUSTR_29; // = HUSTR_29; +//#define HUSTR_30 "level 30: icon of sin" +extern const char *s_HUSTR_30; // = HUSTR_30; + +//#define HUSTR_31 "level 31: wolfenstein" +extern const char *s_HUSTR_31; // = HUSTR_31; +//#define HUSTR_32 "level 32: grosse" +extern const char *s_HUSTR_32; // = HUSTR_32; +extern const char *s_HUSTR_33; + +//#define PHUSTR_1 "level 1: congo" +extern const char *s_PHUSTR_1; // = PHUSTR_1; +//#define PHUSTR_2 "level 2: well of souls" +extern const char *s_PHUSTR_2; // = PHUSTR_2; +//#define PHUSTR_3 "level 3: aztec" +extern const char *s_PHUSTR_3; // = PHUSTR_3; +//#define PHUSTR_4 "level 4: caged" +extern const char *s_PHUSTR_4; // = PHUSTR_4; +//#define PHUSTR_5 "level 5: ghost town" +extern const char *s_PHUSTR_5; // = PHUSTR_5; +//#define PHUSTR_6 "level 6: baron's lair" +extern const char *s_PHUSTR_6; // = PHUSTR_6; +//#define PHUSTR_7 "level 7: caughtyard" +extern const char *s_PHUSTR_7; // = PHUSTR_7; +//#define PHUSTR_8 "level 8: realm" +extern const char *s_PHUSTR_8; // = PHUSTR_8; +//#define PHUSTR_9 "level 9: abattoire" +extern const char *s_PHUSTR_9; // = PHUSTR_9; +//#define PHUSTR_10 "level 10: onslaught" +extern const char *s_PHUSTR_10; // = PHUSTR_10; +//#define PHUSTR_11 "level 11: hunted" +extern const char *s_PHUSTR_11; // = PHUSTR_11; + +//#define PHUSTR_12 "level 12: speed" +extern const char *s_PHUSTR_12; // = PHUSTR_12; +//#define PHUSTR_13 "level 13: the crypt" +extern const char *s_PHUSTR_13; // = PHUSTR_13; +//#define PHUSTR_14 "level 14: genesis" +extern const char *s_PHUSTR_14; // = PHUSTR_14; +//#define PHUSTR_15 "level 15: the twilight" +extern const char *s_PHUSTR_15; // = PHUSTR_15; +//#define PHUSTR_16 "level 16: the omen" +extern const char *s_PHUSTR_16; // = PHUSTR_16; +//#define PHUSTR_17 "level 17: compound" +extern const char *s_PHUSTR_17; // = PHUSTR_17; +//#define PHUSTR_18 "level 18: neurosphere" +extern const char *s_PHUSTR_18; // = PHUSTR_18; +//#define PHUSTR_19 "level 19: nme" +extern const char *s_PHUSTR_19; // = PHUSTR_19; +//#define PHUSTR_20 "level 20: the death domain" +extern const char *s_PHUSTR_20; // = PHUSTR_20; + +//#define PHUSTR_21 "level 21: slayer" +extern const char *s_PHUSTR_21; // = PHUSTR_21; +//#define PHUSTR_22 "level 22: impossible mission" +extern const char *s_PHUSTR_22; // = PHUSTR_22; +//#define PHUSTR_23 "level 23: tombstone" +extern const char *s_PHUSTR_23; // = PHUSTR_23; +//#define PHUSTR_24 "level 24: the final frontier" +extern const char *s_PHUSTR_24; // = PHUSTR_24; +//#define PHUSTR_25 "level 25: the temple of darkness" +extern const char *s_PHUSTR_25; // = PHUSTR_25; +//#define PHUSTR_26 "level 26: bunker" +extern const char *s_PHUSTR_26; // = PHUSTR_26; +//#define PHUSTR_27 "level 27: anti-christ" +extern const char *s_PHUSTR_27; // = PHUSTR_27; +//#define PHUSTR_28 "level 28: the sewers" +extern const char *s_PHUSTR_28; // = PHUSTR_28; +//#define PHUSTR_29 "level 29: odyssey of noises" +extern const char *s_PHUSTR_29; // = PHUSTR_29; +//#define PHUSTR_30 "level 30: the gateway of hell" +extern const char *s_PHUSTR_30; // = PHUSTR_30; + +//#define PHUSTR_31 "level 31: cyberden" +extern const char *s_PHUSTR_31; // = PHUSTR_31; +//#define PHUSTR_32 "level 32: go 2 it" +extern const char *s_PHUSTR_32; // = PHUSTR_32; + +//#define THUSTR_1 "level 1: system control" +extern const char *s_THUSTR_1; // = THUSTR_1; +//#define THUSTR_2 "level 2: human bbq" +extern const char *s_THUSTR_2; // = THUSTR_2; +//#define THUSTR_3 "level 3: power control" +extern const char *s_THUSTR_3; // = THUSTR_3; +//#define THUSTR_4 "level 4: wormhole" +extern const char *s_THUSTR_4; // = THUSTR_4; +//#define THUSTR_5 "level 5: hanger" +extern const char *s_THUSTR_5; // = THUSTR_5; +//#define THUSTR_6 "level 6: open season" +extern const char *s_THUSTR_6; // = THUSTR_6; +//#define THUSTR_7 "level 7: prison" +extern const char *s_THUSTR_7; // = THUSTR_7; +//#define THUSTR_8 "level 8: metal" +extern const char *s_THUSTR_8; // = THUSTR_8; +//#define THUSTR_9 "level 9: stronghold" +extern const char *s_THUSTR_9; // = THUSTR_9; +//#define THUSTR_10 "level 10: redemption" +extern const char *s_THUSTR_10; // = THUSTR_10; +//#define THUSTR_11 "level 11: storage facility" +extern const char *s_THUSTR_11; // = THUSTR_11; + +//#define THUSTR_12 "level 12: crater" +extern const char *s_THUSTR_12; // = THUSTR_12; +//#define THUSTR_13 "level 13: nukage processing" +extern const char *s_THUSTR_13; // = THUSTR_13; +//#define THUSTR_14 "level 14: steel works" +extern const char *s_THUSTR_14; // = THUSTR_14; +//#define THUSTR_15 "level 15: dead zone" +extern const char *s_THUSTR_15; // = THUSTR_15; +//#define THUSTR_16 "level 16: deepest reaches" +extern const char *s_THUSTR_16; // = THUSTR_16; +//#define THUSTR_17 "level 17: processing area" +extern const char *s_THUSTR_17; // = THUSTR_17; +//#define THUSTR_18 "level 18: mill" +extern const char *s_THUSTR_18; // = THUSTR_18; +//#define THUSTR_19 "level 19: shipping/respawning" +extern const char *s_THUSTR_19; // = THUSTR_19; +//#define THUSTR_20 "level 20: central processing" +extern const char *s_THUSTR_20; // = THUSTR_20; + +//#define THUSTR_21 "level 21: administration center" +extern const char *s_THUSTR_21; // = THUSTR_21; +//#define THUSTR_22 "level 22: habitat" +extern const char *s_THUSTR_22; // = THUSTR_22; +//#define THUSTR_23 "level 23: lunar mining project" +extern const char *s_THUSTR_23; // = THUSTR_23; +//#define THUSTR_24 "level 24: quarry" +extern const char *s_THUSTR_24; // = THUSTR_24; +//#define THUSTR_25 "level 25: baron's den" +extern const char *s_THUSTR_25; // = THUSTR_25; +//#define THUSTR_26 "level 26: ballistyx" +extern const char *s_THUSTR_26; // = THUSTR_26; +//#define THUSTR_27 "level 27: mount pain" +extern const char *s_THUSTR_27; // = THUSTR_27; +//#define THUSTR_28 "level 28: heck" +extern const char *s_THUSTR_28; // = THUSTR_28; +//#define THUSTR_29 "level 29: river styx" +extern const char *s_THUSTR_29; // = THUSTR_29; +//#define THUSTR_30 "level 30: last call" +extern const char *s_THUSTR_30; // = THUSTR_30; + +//#define THUSTR_31 "level 31: pharaoh" +extern const char *s_THUSTR_31; // = THUSTR_31; +//#define THUSTR_32 "level 32: caribbean" +extern const char *s_THUSTR_32; // = THUSTR_32; + +//#define HUSTR_CHATMACRO1 "I'm ready to kick butt!" +extern const char *s_HUSTR_CHATMACRO1; // = HUSTR_CHATMACRO1; +//#define HUSTR_CHATMACRO2 "I'm OK." +extern const char *s_HUSTR_CHATMACRO2; // = HUSTR_CHATMACRO2; +//#define HUSTR_CHATMACRO3 "I'm not looking too good!" +extern const char *s_HUSTR_CHATMACRO3; // = HUSTR_CHATMACRO3; +//#define HUSTR_CHATMACRO4 "Help!" +extern const char *s_HUSTR_CHATMACRO4; // = HUSTR_CHATMACRO4; +//#define HUSTR_CHATMACRO5 "You suck!" +extern const char *s_HUSTR_CHATMACRO5; // = HUSTR_CHATMACRO5; +//#define HUSTR_CHATMACRO6 "Next time, scumbag..." +extern const char *s_HUSTR_CHATMACRO6; // = HUSTR_CHATMACRO6; +//#define HUSTR_CHATMACRO7 "Come here!" +extern const char *s_HUSTR_CHATMACRO7; // = HUSTR_CHATMACRO7; +//#define HUSTR_CHATMACRO8 "I'll take care of it." +extern const char *s_HUSTR_CHATMACRO8; // = HUSTR_CHATMACRO8; +//#define HUSTR_CHATMACRO9 "Yes" +extern const char *s_HUSTR_CHATMACRO9; // = HUSTR_CHATMACRO9; +//#define HUSTR_CHATMACRO0 "No" +extern const char *s_HUSTR_CHATMACRO0; // = HUSTR_CHATMACRO0; + +//#define HUSTR_TALKTOSELF1 "You mumble to yourself" +extern const char *s_HUSTR_TALKTOSELF1; // = HUSTR_TALKTOSELF1; +//#define HUSTR_TALKTOSELF2 "Who's there?" +extern const char *s_HUSTR_TALKTOSELF2; // = HUSTR_TALKTOSELF2; +//#define HUSTR_TALKTOSELF3 "You scare yourself" +extern const char *s_HUSTR_TALKTOSELF3; // = HUSTR_TALKTOSELF3; +//#define HUSTR_TALKTOSELF4 "You start to rave" +extern const char *s_HUSTR_TALKTOSELF4; // = HUSTR_TALKTOSELF4; +//#define HUSTR_TALKTOSELF5 "You've lost it..." +extern const char *s_HUSTR_TALKTOSELF5; // = HUSTR_TALKTOSELF5; + +//#define HUSTR_MESSAGESENT "[Message Sent]" +extern const char *s_HUSTR_MESSAGESENT; // = HUSTR_MESSAGESENT; + +// The following should NOT be changed unless it seems +// just AWFULLY necessary + +//#define HUSTR_PLRGREEN "Green: " +extern const char *s_HUSTR_PLRGREEN; // = HUSTR_PLRGREEN; +//#define HUSTR_PLRINDIGO "Indigo: " +extern const char *s_HUSTR_PLRINDIGO; // = HUSTR_PLRINDIGO; +//#define HUSTR_PLRBROWN "Brown: " +extern const char *s_HUSTR_PLRBROWN; // = HUSTR_PLRBROWN; +//#define HUSTR_PLRRED "Red: " +extern const char *s_HUSTR_PLRRED; // = HUSTR_PLRRED; + +// +// AM_map.C +// + +//#define AMSTR_FOLLOWON "Follow Mode ON" +extern const char* s_AMSTR_FOLLOWON; // = AMSTR_FOLLOWON; +//#define AMSTR_FOLLOWOFF "Follow Mode OFF" +extern const char* s_AMSTR_FOLLOWOFF; // = AMSTR_FOLLOWOFF; + +//#define AMSTR_GRIDON "Grid ON" +extern const char* s_AMSTR_GRIDON; // = AMSTR_GRIDON; +//#define AMSTR_GRIDOFF "Grid OFF" +extern const char* s_AMSTR_GRIDOFF; // = AMSTR_GRIDOFF; + +//#define AMSTR_MARKEDSPOT "Marked Spot" +extern const char* s_AMSTR_MARKEDSPOT; // = AMSTR_MARKEDSPOT; +//#define AMSTR_MARKSCLEARED "All Marks Cleared" +extern const char* s_AMSTR_MARKSCLEARED; // = AMSTR_MARKSCLEARED; + +// CPhipps - automap rotate & overlay +extern const char* s_AMSTR_ROTATEON; +extern const char* s_AMSTR_ROTATEOFF; +extern const char* s_AMSTR_OVERLAYON; +extern const char* s_AMSTR_OVERLAYOFF; +// e6y: textured automap +extern const char* s_AMSTR_TEXTUREDON; +extern const char* s_AMSTR_TEXTUREDOFF; + +// +// ST_stuff.C +// + +//#define STSTR_MUS "Music Change" +extern const char* s_STSTR_MUS; // = STSTR_MUS; +//#define STSTR_NOMUS "IMPOSSIBLE SELECTION" +extern const char* s_STSTR_NOMUS; // = STSTR_NOMUS; +//#define STSTR_DQDON "Degreelessness Mode On" +extern const char* s_STSTR_DQDON; // = STSTR_DQDON; +//#define STSTR_DQDOFF "Degreelessness Mode Off" +extern const char* s_STSTR_DQDOFF; // = STSTR_DQDOFF; + +//#define STSTR_KFAADDED "Very Happy Ammo Added" +extern const char* s_STSTR_KFAADDED; // = STSTR_KFAADDED; +//#define STSTR_FAADDED "Ammo (no keys) Added" +extern const char* s_STSTR_FAADDED; // = STSTR_FAADDED; + +//#define STSTR_NCON "No Clipping Mode ON" +extern const char* s_STSTR_NCON; // = STSTR_NCON; +//#define STSTR_NCOFF "No Clipping Mode OFF" +extern const char* s_STSTR_NCOFF; // = STSTR_NCOFF; + +//#define STSTR_BEHOLD "inVuln, Str, Inviso, Rad, Allmap, or Lite-amp" +extern const char* s_STSTR_BEHOLD; // = STSTR_BEHOLD; +//#define STSTR_BEHOLDX "Power-up Toggled" +extern const char* s_STSTR_BEHOLDX; // = STSTR_BEHOLDX; + +//#define STSTR_CHOPPERS "... doesn't suck - GM" +extern const char* s_STSTR_CHOPPERS; // = STSTR_CHOPPERS; +//#define STSTR_CLEV "Changing Level..." +extern const char* s_STSTR_CLEV; // = STSTR_CLEV; + +// +// 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" +*/ +extern const char* s_E1TEXT; // = E1TEXT; + + +/* +#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." +*/ +extern const char* s_E2TEXT; // = E2TEXT; + + +/* +#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 ..." +*/ +extern const char* s_E3TEXT; // = E3TEXT; + + +/* +#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!" +*/ +extern const char* s_E4TEXT; // = E4TEXT; + + +// 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." +*/ +extern const char* s_C1TEXT; // = C1TEXT; + +// 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." +*/ +extern const char* s_C2TEXT; // = C2TEXT; + + +// 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?" +*/ +extern const char* s_C3TEXT; // = C3TEXT; + + +// 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" +*/ +extern const char* s_C4TEXT; // = C4TEXT; + + + +// 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." +*/ +extern const char* s_C5TEXT; // = C5TEXT; + + +// 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" +*/ +extern const char* s_C6TEXT; // = C6TEXT; + + +// 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." +*/ +extern const char* s_P1TEXT; // = P1TEXT; + + +// 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." +*/ +extern const char* s_P2TEXT; // = P2TEXT; + + +// 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!" +*/ +extern const char* s_P3TEXT; // = P3TEXT; + +// 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 ..." +*/ +extern const char* s_P4TEXT; // = P4TEXT; + +// 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." +*/ +extern const char* s_P5TEXT; // = P5TEXT; + +// 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." +*/ +extern const char* s_P6TEXT; // = P6TEXT; + + +/* +#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." +*/ +extern const char* s_T1TEXT; // = T1TEXT; + + +/* +#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." +*/ +extern const char* s_T2TEXT; // = T2TEXT; + + +/* +#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." +*/ +extern const char* s_T3TEXT; // = T3TEXT; + +/* +#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." +*/ +extern const char* s_T4TEXT; // = T4TEXT; + + +/* +#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.." +*/ +extern const char* s_T5TEXT; // = T5TEXT; + + +/* +#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." +*/ +extern const char* s_T6TEXT; // = T6TEXT; + +// +// Character cast strings F_FINALE.C +// +//#define CC_ZOMBIE "ZOMBIEMAN" +extern const char* s_CC_ZOMBIE; // = CC_ZOMBIE; +//#define CC_SHOTGUN "SHOTGUN GUY" +extern const char* s_CC_SHOTGUN; // = CC_SHOTGUN; +//#define CC_HEAVY "HEAVY WEAPON DUDE" +extern const char* s_CC_HEAVY; // = CC_HEAVY; +//#define CC_IMP "IMP" +extern const char* s_CC_IMP; // = CC_IMP; +//#define CC_DEMON "DEMON" +extern const char* s_CC_DEMON; // = CC_DEMON; +//#define CC_LOST "LOST SOUL" +extern const char* s_CC_LOST; // = CC_LOST; +//#define CC_CACO "CACODEMON" +extern const char* s_CC_CACO; // = CC_CACO; +//#define CC_HELL "HELL KNIGHT" +extern const char* s_CC_HELL; // = CC_HELL; +//#define CC_BARON "BARON OF HELL" +extern const char* s_CC_BARON; // = CC_BARON; +//#define CC_ARACH "ARACHNOTRON" +extern const char* s_CC_ARACH; // = CC_ARACH; +//#define CC_PAIN "PAIN ELEMENTAL" +extern const char* s_CC_PAIN; // = CC_PAIN; +//#define CC_REVEN "REVENANT" +extern const char* s_CC_REVEN; // = CC_REVEN; +//#define CC_MANCU "MANCUBUS" +extern const char* s_CC_MANCU; // = CC_MANCU; +//#define CC_ARCH "ARCH-VILE" +extern const char* s_CC_ARCH; // = CC_ARCH; +//#define CC_SPIDER "THE SPIDER MASTERMIND" +extern const char* s_CC_SPIDER; // = CC_SPIDER; +//#define CC_CYBER "THE CYBERDEMON" +extern const char* s_CC_CYBER; // = CC_CYBER; +//#define CC_HERO "OUR HERO" +extern const char* s_CC_HERO; // = CC_HERO; + +// Ty 03/30/98 - new substitutions for background textures during int screens +// char* bgflatE1 = "FLOOR4_8"; +extern const char* bgflatE1; +// char* bgflatE2 = "SFLR6_1"; +extern const char* bgflatE2; +// char* bgflatE3 = "MFLR8_4"; +extern const char* bgflatE3; +// char* bgflatE4 = "MFLR8_3"; +extern const char* bgflatE4; + +// char* bgflat06 = "SLIME16"; +extern const char* bgflat06; +// char* bgflat11 = "RROCK14"; +extern const char* bgflat11; +// char* bgflat20 = "RROCK07"; +extern const char* bgflat20; +// char* bgflat30 = "RROCK17"; +extern const char* bgflat30; +// char* bgflat15 = "RROCK13"; +extern const char* bgflat15; +// char* bgflat31 = "RROCK19"; +extern const char* bgflat31; + +// char* bgcastcall = "BOSSBACK"; // panel behind cast call +extern const char* bgcastcall; + +// ignored if blank, general purpose startup announcements +// char* startup1 = ""; +extern const char* startup1; +// char* startup2 = ""; +extern const char* startup2; +// char* startup3 = ""; +extern const char* startup3; +// char* startup4 = ""; +extern const char* startup4; +// char* startup5 = ""; +extern const char* startup5; + +// from g_game.c, prefix for savegame name like "boomsav" +extern const char* savegamename; + +void D_BuildBEXTables(void); +void deh_changeCompTranslucency(void); +void deh_applyCompatibility(void); + +#endif diff --git a/src/d_englsh.h b/src/d_englsh.h new file mode 100644 index 0000000..9aac56b --- /dev/null +++ b/src/d_englsh.h @@ -0,0 +1,711 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Printed strings for translation. + * English language support (default). + * See dstrings.h for suggestions about foreign language BEX support + * + *-----------------------------------------------------------------------------*/ + +#ifndef __D_ENGLSH__ +#define __D_ENGLSH__ + +/* 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 RESTARTLEVEL "restart the level?\n\n"PRESSYN + +#define DOSY "(press y to quit)" + +#define DETAILHI "High detail" +#define DETAILLO "Low detail" +#define GAMMALVL0 "Gamma correction OFF" +#define GAMMALVL1 "Gamma correction level 1" +#define GAMMALVL2 "Gamma correction level 2" +#define GAMMALVL3 "Gamma correction level 3" +#define GAMMALVL4 "Gamma correction level 4" +#define EMPTYSTRING "empty slot" + +/* p_inter.c */ +#define GOTARMOR "Picked up the armor." +#define GOTMEGA "Picked up the MegaArmor!" +#define GOTHTHBONUS "Picked up a health bonus." +#define GOTARMBONUS "Picked up an armor bonus." +#define GOTSTIM "Picked up a stimpack." +#define GOTMEDINEED "Picked up a medikit that you REALLY need!" +#define GOTMEDIKIT "Picked up a medikit." +#define GOTSUPER "Supercharge!" + +#define GOTBLUECARD "Picked up a blue keycard." +#define GOTYELWCARD "Picked up a yellow keycard." +#define GOTREDCARD "Picked up a red keycard." +#define GOTBLUESKUL "Picked up a blue skull key." +#define GOTYELWSKUL "Picked up a yellow skull key." +#define GOTREDSKULL "Picked up a red skull key." + +#define GOTINVUL "Invulnerability!" +#define GOTBERSERK "Berserk!" +#define GOTINVIS "Partial Invisibility" +#define GOTSUIT "Radiation Shielding Suit" +#define GOTMAP "Computer Area Map" +#define GOTVISOR "Light Amplification Visor" +#define GOTMSPHERE "MegaSphere!" + +#define GOTCLIP "Picked up a clip." +#define GOTCLIPBOX "Picked up a box of bullets." +#define GOTROCKET "Picked up a rocket." +#define GOTROCKBOX "Picked up a box of rockets." +#define GOTCELL "Picked up an energy cell." +#define GOTCELLBOX "Picked up an energy cell pack." +#define GOTSHELLS "Picked up 4 shotgun shells." +#define GOTSHELLBOX "Picked up a box of shotgun shells." +#define GOTBACKPACK "Picked up a backpack full of ammo!" + +#define GOTBFG9000 "You got the BFG9000! Oh, yes." +#define GOTCHAINGUN "You got the chaingun!" +#define GOTCHAINSAW "A chainsaw! Find some meat!" +#define GOTLAUNCHER "You got the rocket launcher!" +#define GOTPLASMA "You got the plasma gun!" +#define GOTSHOTGUN "You got the shotgun!" +#define GOTSHOTGUN2 "You got the super shotgun!" + +/* p_doors.c */ +#define PD_BLUEO "You need a blue key to activate this object" +#define PD_REDO "You need a red key to activate this object" +#define PD_YELLOWO "You need a yellow key to activate this object" +#define PD_BLUEK "You need a blue key to open this door" +#define PD_REDK "You need a red key to open this door" +#define PD_YELLOWK "You need a yellow key to open this door" +/* jff 02/05/98 Create messages specific to card and skull keys */ +#define PD_BLUEC "You need a blue card to open this door" +#define PD_REDC "You need a red card to open this door" +#define PD_YELLOWC "You need a yellow card to open this door" +#define PD_BLUES "You need a blue skull to open this door" +#define PD_REDS "You need a red skull to open this door" +#define PD_YELLOWS "You need a yellow skull to open this door" +#define PD_ANY "Any key will open this door" +#define PD_ALL3 "You need all three keys to open this door" +#define PD_ALL6 "You need all six keys 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 HUSTR_33 "level 33: betray" + +#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 "Player 1: " +#define HUSTR_PLRINDIGO "Player 2: " +#define HUSTR_PLRBROWN "Player 3: " +#define HUSTR_PLRRED "Player 4: " + +#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" + +#define AMSTR_ROTATEON "Rotate Mode ON" +#define AMSTR_ROTATEOFF "Rotate Mode OFF" + +#define AMSTR_OVERLAYON "Overlay Mode ON" +#define AMSTR_OVERLAYOFF "Overlay Mode OFF" + +#define AMSTR_TEXTUREDON "Textured Mode ON" +#define AMSTR_TEXTUREDOFF "Textured Mode OFF" + +/* 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..." + +#define STSTR_COMPON "Compatibility Mode On" /* phares */ +#define STSTR_COMPOFF "Compatibility Mode Off" /* phares */ + +/* 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" + +/*** Plutonia ***/ +/* 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." + +/*** TNT: Evilution ***/ + +#define T1TEXT \ + "You've fought your way out of the infested\n"\ + "experimental labs. It seems that UAC has\n"\ + "once again gulped it down. With their\n"\ + "high turnover, it must be hard for poor\n"\ + "old UAC to buy corporate health insurance\n"\ + "nowadays..\n"\ + "\n"\ + "Ahead lies the military complex, now\n"\ + "swarming with diseased horrors hot to get\n"\ + "their teeth into you. With luck, the\n"\ + "complex still has some warlike ordnance\n"\ + "laying around." + + +#define T2TEXT \ + "You hear the grinding of heavy machinery\n"\ + "ahead. You sure hope they're not stamping\n"\ + "out new hellspawn, but you're ready to\n"\ + "ream out a whole herd if you have to.\n"\ + "They might be planning a blood feast, but\n"\ + "you feel about as mean as two thousand\n"\ + "maniacs packed into one mad killer.\n"\ + "\n"\ + "You don't plan to go down easy." + + +#define T3TEXT \ + "The vista opening ahead looks real damn\n"\ + "familiar. Smells familiar, too -- like\n"\ + "fried excrement. You didn't like this\n"\ + "place before, and you sure as hell ain't\n"\ + "planning to like it now. The more you\n"\ + "brood on it, the madder you get.\n"\ + "Hefting your gun, an evil grin trickles\n"\ + "onto your face. Time to take some names." + +#define T4TEXT \ + "Suddenly, all is silent, from one horizon\n"\ + "to the other. The agonizing echo of Hell\n"\ + "fades away, the nightmare sky turns to\n"\ + "blue, the heaps of monster corpses start \n"\ + "to evaporate along with the evil stench \n"\ + "that filled the air. Jeeze, maybe you've\n"\ + "done it. Have you really won?\n"\ + "\n"\ + "Something rumbles in the distance.\n"\ + "A blue light begins to glow inside the\n"\ + "ruined skull of the demon-spitter." + + +#define T5TEXT \ + "What now? Looks totally different. Kind\n"\ + "of like King Tut's condo. Well,\n"\ + "whatever's here can't be any worse\n"\ + "than usual. Can it? Or maybe it's best\n"\ + "to let sleeping gods lie.." + + +#define T6TEXT \ + "Time for a vacation. You've burst the\n"\ + "bowels of hell and by golly you're ready\n"\ + "for a break. You mutter to yourself,\n"\ + "Maybe someone else can kick Hell's ass\n"\ + "next time around. Ahead lies a quiet town,\n"\ + "with peaceful flowing water, quaint\n"\ + "buildings, and presumably no Hellspawn.\n"\ + "\n"\ + "As you step off the transport, you hear\n"\ + "the stomp of a cyberdemon's iron shoe." + + + +/* + * Character cast strings F_FINALE.C + */ +#define CC_ZOMBIE "ZOMBIEMAN" +#define CC_SHOTGUN "SHOTGUN GUY" +#define CC_HEAVY "HEAVY WEAPON DUDE" +#define CC_IMP "IMP" +#define CC_DEMON "DEMON" +#define CC_LOST "LOST SOUL" +#define CC_CACO "CACODEMON" +#define CC_HELL "HELL KNIGHT" +#define CC_BARON "BARON OF HELL" +#define CC_ARACH "ARACHNOTRON" +#define CC_PAIN "PAIN ELEMENTAL" +#define CC_REVEN "REVENANT" +#define CC_MANCU "MANCUBUS" +#define CC_ARCH "ARCH-VILE" +#define CC_SPIDER "THE SPIDER MASTERMIND" +#define CC_CYBER "THE CYBERDEMON" +#define CC_HERO "OUR HERO" + + +#endif diff --git a/src/d_event.h b/src/d_event.h new file mode 100644 index 0000000..76551fa --- /dev/null +++ b/src/d_event.h @@ -0,0 +1,129 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Event information structures. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __D_EVENT__ +#define __D_EVENT__ + + +#include "doomtype.h" + + +// +// Event handling. +// + +// Input event types. +typedef enum +{ + ev_keydown, + ev_keyup, + ev_mouse, + ev_mousemotion, + ev_joystick +} evtype_t; + +// Event structure. +typedef struct +{ + evtype_t type; + int data1; // keys / mouse/joystick buttons + int data2; // mouse/joystick x move + int data3; // mouse/joystick y move +} event_t; + + +typedef enum +{ + ga_nothing, + ga_loadlevel, + ga_newgame, + ga_loadgame, + ga_savegame, + ga_playdemo, + ga_completed, + ga_victory, + ga_worlddone, + ga_screenshot +} gameaction_t; + + + +// +// Button/action code definitions. +// +typedef enum +{ + // Press "Fire". + BT_ATTACK = 1, + + // Use button, to open doors, activate switches. + BT_USE = 2, + + // Flag: game events, not really buttons. + BT_SPECIAL = 128, + BT_SPECIALMASK = 3, + + // Flag, weapon change pending. + // If true, the next 4 bits hold weapon num. + BT_CHANGE = 4, + + // The 4bit weapon mask and shift, convenience. +BT_WEAPONMASK_OLD = (8+16+32),//e6y + BT_WEAPONMASK = (8+16+32+64), // extended to pick up SSG // phares + BT_WEAPONSHIFT = 3, + + // Special events + BTS_LOADGAME = 0, // Loads a game + // Pause the game. + BTS_PAUSE = 1, + // Save the game at each console. + BTS_SAVEGAME = 2, + BTS_RESTARTLEVEL= 3, // Restarts the current level + + // Savegame slot numbers occupy the second byte of buttons. + BTS_SAVEMASK = (4+8+16), + BTS_SAVESHIFT = 2, + + // Demo joined. + BT_JOIN = 64 +} buttoncode_t; + + +// +// GLOBAL VARIABLES +// + +extern gameaction_t gameaction; + +#endif diff --git a/src/d_ipxgate.c b/src/d_ipxgate.c new file mode 100644 index 0000000..aad28b6 --- /dev/null +++ b/src/d_ipxgate.c @@ -0,0 +1,291 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "protocol.h" + +#define BACKUPTICS 12 +#define NCMD_EXIT 0x80000000 +#define NCMD_RETRANSMIT 0x40000000 +#define NCMD_SETUP 0x20000000 +#define NCMD_KILL 0x10000000 // kill game +#define NCMD_CHECKSUM 0x0fffffff + +typedef struct +{ + short gameid; // so multiple games can setup at once + short drone; + short nodesfound; + short nodeswanted; +} setupdata_t; + +typedef struct +a +{ + // High bit is retransmit request. + unsigned checksum; + // Only valid if NCMD_RETRANSMIT. + byte retransmitfrom; + + byte starttic; + byte player; + byte numtics; + ticcmd_t cmds[BACKUPTICS]; +} doomdata_t; + +typedef struct +{ + signed int tic; + union altu { + setupdata_t s; + unsigned char data[100]; + doomdata_t d; + } u; +} ipxpacket_t; + +int nodes; + +unsigned short port = 0x869b; + +int ipx_socket(void) { + int s = socket(PF_IPX,SOCK_DGRAM,0); + struct sockaddr_ipx sa; + if (s == -1) { + fprintf(stderr,"socket(PF_PIPX): %s\n",strerror(errno)); + exit(-1); + } + memset(&sa,0,sizeof(sa)); + memset(sa.sipx_node,0xff,sizeof(sa.sipx_node)); + sa.sipx_port = htons(port); + if (bind(s,(struct sockaddr*)&sa,sizeof(sa)) == -1) { + fprintf(stderr,"bind(%d): %s\n",port,strerror(errno)); + exit(-1); + } + return s; +} + +int udp_socket(const char* ip) { + struct sockaddr_in sa; + int s = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP); + + if (s == -1) { + fprintf(stderr,"socket(PF_INET): %s\n", strerror(errno)); + exit(-1); + } + sa.sin_family=PF_INET; + inet_aton(ip,&sa.sin_addr); + sa.sin_port = htons(5030); + + if (connect(s,(struct sockaddr*)&sa,sizeof sa) == -1) { + fprintf(stderr,"connect(%s:%d): %s\n", ip, 5030, strerror(errno)); + exit(-1); + } + return s; +} + +static byte ChecksumPacket(const packet_header_t* buffer, size_t len) +{ + const byte* p = (void*)buffer; + byte sum = 0; + + if (len==0) + return 0; + + while (p++, --len) + sum += *p; + + return sum; +} + +// +// Checksum +// +unsigned NetbufferChecksum (void* p, size_t l) +{ + unsigned c; + + c = 0x1234567; + + l = l/4; + for (int i=0 ; i= -64 && delta <= 64) + return (maketic&~0xff) + low; + if (delta > 64) + return (maketic&~0xff) - 256 + low; + if (delta < -64) + return (maketic&~0xff) + 256 + low; + fprintf(stderr,"ExpandTics strange value %i at maketic %i\n",low,maketic); + exit(-2); +} + +void send_udp_packet(enum packet_type_e type, unsigned tic, void* data, size_t len) { + packet_header_t* p = calloc(sizeof(packet_header_t)+len+1,1); + p->tic = doom_htonl(basetic = tic); p->type = type; + if (!data) { + data = (void*)&consoleplayer; len = 1; + } + memcpy(((char*)p)+sizeof(*p),data,len); + p->checksum = ChecksumPacket(p,sizeof(packet_header_t)+len); + write(udps,p,sizeof(packet_header_t)+len+1); +} + +int connected; +int ipxcounter; + +void ipx_receive(int s) { + ipxpacket_t buf; + int rc; + struct sockaddr from; + size_t sl = sizeof(from); + rc = recvfrom(s,&buf,sizeof buf,0,&from,&sl); + if (rc == -1) { + fprintf(stderr,"read(ipx): %s\n", strerror(errno)); + exit(-2); + } + if (rc > 0) { + if (buf.tic == -1) { + // Setup packet + if (!connected++) { + connect(s,&from,sl); + send_udp_packet(PKT_INIT,0,NULL,0); + } + } else { + if (buf.u.d.checksum & NCMD_SETUP) { + printf("setup packet, dropped\n"); + } else if (buf.u.d.checksum & NCMD_EXIT) { + send_udp_packet(PKT_QUIT,buf.u.d.starttic,NULL,0); + exit(0); + } else if ((buf.u.d.checksum & NCMD_CHECKSUM) == buf.u.d.checksum) { + // No flags, normal game packet + char outbuf[100]; + int tics; + outbuf[0] = tics = buf.u.d.numtics; + outbuf[1] = buf.u.d.player; + for (int i=0; i< tics; i++) + TicToRaw(outbuf+2+i*sizeof(ticcmd_t),&buf.u.d.cmds[i]); + send_udp_packet(PKT_TICC, ExpandTics(buf.u.d.starttic, basetic), outbuf, 2+tics*sizeof(ticcmd_t)); + } + } + } +} + +void udp_receive(int s) { + size_t len = 1024; + packet_header_t *p = malloc(len); + int rc; + + rc = read(s,p,len); + if (rc < 0) { + fprintf(stderr,"read(udp): %s\n", strerror(errno)); + exit(-2); + } + if (rc > 0) { + switch (p->type) { + case PKT_SETUP: + { + struct setup_packet_s *sinfo = (void*)(p+1); + consoleplayer = sinfo->yourplayer; + send_udp_packet(PKT_GO,0,NULL,0); + write(ipxs,"\xff\xff\xff\xff\x00\x00\x00\x00\x02\x00\x02\x00\x00\x00\x00\x00",16); + } + break; + case PKT_GO: + { + ipxpacket_t pkt; + memset(&pkt,0,sizeof(pkt)); + pkt.tic = ipxcounter++; + pkt.u.d.player = consoleplayer^1; + pkt.u.d.starttic = 0; + pkt.u.d.numtics = 0; + pkt.u.d.retransmitfrom = 0; + pkt.u.d.checksum = NetbufferChecksum(&pkt.u.d.retransmitfrom, 4); + write(ipxs,&pkt,16); + } + break; + case PKT_TICS: + { + ipxpacket_t pkt; + int tic = doom_ntohl(p->tic); + byte *pp = (void*)(p+1); + int tics = *pp++; + memset(&pkt,0,sizeof(pkt)); + size_t len; + + pkt.tic = ipxcounter++; + pkt.u.d.starttic = tic; + pkt.u.d.player = (consoleplayer == 0 ? 1 : 0); + pkt.u.d.numtics = tics; + + for (int t=0; t0) { + if (FD_ISSET(ipxs,&fds)) + ipx_receive(ipxs); + if (FD_ISSET(udps,&fds)) + udp_receive(udps); + } + } +} + +int main(int argc, char**argv) { + ipxs = ipx_socket(); + udps = udp_socket(argv[1]); + loop(ipxs,udps); + return 0; +} + diff --git a/src/d_items.c b/src/d_items.c new file mode 100644 index 0000000..c267699 --- /dev/null +++ b/src/d_items.c @@ -0,0 +1,168 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Something to do with weapon sprite frames. Don't ask me. + * + *----------------------------------------------------------------------------- + */ + +// We are referring to sprite numbers. +#include "doomtype.h" +#include "info.h" + +#ifdef __GNUG__ +#pragma implementation "d_items.h" +#endif +#include "d_items.h" + + +// +// PSPRITE ACTIONS for waepons. +// This struct controls the weapon animations. +// +// Each entry is: +// ammo/amunition type +// upstate +// downstate +// readystate +// atkstate, i.e. attack/fire/hit frame +// flashstate, muzzle flash +// +weaponinfo_t weaponinfo[NUMWEAPONS+2] = +{ + { + // 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 + }, + + // dseg03:00082D90 weaponinfo_t <5, 46h, 45h, 43h, 47h, 0> + // dseg03:00082D90 weaponinfo_t <1, 22h, 21h, 20h, 23h, 2Fh> + // dseg03:00082E68 animdefs dd 0 ; istexture + // dseg03:00082E68 db 'N', 'U', 'K', 'A', 'G', 'E', '3', 2 dup(0); endname + // dseg03:00082E68 db 'N', 'U', 'K', 'A', 'G', 'E', '1', 2 dup(0); startname + // dseg03:00082E68 dd 8 ; speed + // dseg03:00082E68 dd 0 ; istexture + { + // ololo weapon + 0, + S_NULL, // states are not used for emulation of weaponinfo overrun + S_NULL, + S_NULL, + S_NULL, + S_NULL + }, + { + // preved medved weapon + 0, + S_NULL, + S_NULL, + S_NULL, + S_NULL, + S_NULL + }, +}; + +int ammopershot[NUMWEAPONS+2] = {0, 1, 1, 1, 1, 1, 40, 0, 2, 0, 0}; diff --git a/src/d_items.h b/src/d_items.h new file mode 100644 index 0000000..c061880 --- /dev/null +++ b/src/d_items.h @@ -0,0 +1,60 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Items: key cards, artifacts, weapon, ammunition. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __D_ITEMS__ +#define __D_ITEMS__ + +#include "doomdef.h" + +#ifdef __GNUG__ +#pragma interface +#endif + + +/* Weapon info: sprite frames, ammunition use. */ +typedef struct +{ + ammotype_t ammo; + int upstate; + int downstate; + int readystate; + int atkstate; + int flashstate; + +} weaponinfo_t; + +extern weaponinfo_t weaponinfo[NUMWEAPONS+2]; +extern int ammopershot[NUMWEAPONS+2]; + +#endif diff --git a/src/d_main.c b/src/d_main.c new file mode 100644 index 0000000..4e42be6 --- /dev/null +++ b/src/d_main.c @@ -0,0 +1,2350 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * DOOM main program (D_DoomMain) and game loop (D_DoomLoop), + * plus functions to determine game mode (shareware, registered), + * parse command line parameters, configure game parameters (turbo), + * and call the startup functions. + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "SDL_timer.h" + +#ifdef _MSC_VER +#include +#include +#else +#include +#endif +#include +#include +#include + +#include "doomdef.h" +#include "doomtype.h" +#include "doomstat.h" +#include "d_net.h" +#include "dstrings.h" +#include "sounds.h" +#include "z_zone.h" +#include "w_wad.h" +#include "s_sound.h" +#include "v_video.h" +#include "f_finale.h" +#include "f_wipe.h" +#include "m_argv.h" +#include "m_misc.h" +#include "m_menu.h" +#include "p_checksum.h" +#include "i_main.h" +#include "i_system.h" +#include "i_sound.h" +#include "i_video.h" +#include "g_game.h" +#include "hu_stuff.h" +#include "wi_stuff.h" +#include "st_stuff.h" +#include "am_map.h" +#include "p_setup.h" +#include "r_draw.h" +#include "r_main.h" +#include "r_fps.h" +#include "d_main.h" +#include "d_deh.h" // Ty 04/08/98 - Externalizations +#include "lprintf.h" // jff 08/03/98 - declaration of lprintf +#include "am_map.h" +#include "umapinfo.h" +#include "statdump.h" + +//e6y +#include "r_demo.h" +#include "e6y.h" +#ifdef USE_WINDOWS_LAUNCHER +#include "e6y_launcher.h" +#endif + +// NSM +#include "i_capture.h" + +#include "i_glob.h" + +#include "m_io.h" + +void GetFirstMap(int *ep, int *map); // Ty 08/29/98 - add "-warp x" functionality +static void D_PageDrawer(void); + +// CPhipps - removed wadfiles[] stuff + +dboolean devparm; // started game with -devparm + +// jff 1/24/98 add new versions of these variables to remember command line +dboolean clnomonsters; // checkparm of -nomonsters +dboolean clrespawnparm; // checkparm of -respawn +dboolean clfastparm; // checkparm of -fast +// jff 1/24/98 end definition of command line version of play mode switches + +dboolean nomonsters; // working -nomonsters +dboolean respawnparm; // working -respawn +dboolean fastparm; // working -fast + +dboolean singletics = false; // debug flag to cancel adaptiveness + +//jff 1/22/98 parms for disabling music and sound +dboolean nosfxparm; +dboolean nomusicparm; +dboolean umapinfo_loaded; + +//jff 4/18/98 +extern dboolean inhelpscreens; +extern dboolean BorderNeedRefresh; + +skill_t startskill; +int startepisode; +int startmap; +dboolean autostart; +FILE *debugfile; +int ffmap; + +dboolean advancedemo; + +char *basesavegame; // killough 2/16/98: savegame directory + +//jff 4/19/98 list of standard IWAD names +const char *const standard_iwads[]= +{ + "doom2f.wad", + "doom2.wad", + "plutonia.wad", + "tnt.wad", + + "doom.wad", + "doom1.wad", + "doomu.wad", /* CPhipps - alow doomu.wad */ + + "freedoom2.wad", /* wart@kobold.org: added freedoom for Fedora Extras */ + "freedoom1.wad", + "freedm.wad", + + "hacx.wad", + "chex.wad", + "rekkrsa.wad", + + "bfgdoom2.wad", + "bfgdoom.wad", +}; +//e6y static +const int nstandard_iwads = sizeof standard_iwads/sizeof*standard_iwads; + +/* + * D_PostEvent - Event handling + * + * Called by I/O functions when an event is received. + * Try event handlers for each code area in turn. + * cph - in the true spirit of the Boom source, let the + * short ciruit operator madness begin! + */ + +void D_PostEvent(event_t *ev) +{ + /* cph - suppress all input events at game start + * FIXME: This is a lousy kludge */ + + // e6y + // Is this condition needed here? + // Moved to I_StartTic() + // if (gametic < 3) return; + + // Allow only sensible keys during skipping + if (doSkip) + { + if (ev->type == ev_keydown || ev->type == ev_keyup) + { + if (ev->data1 == key_quit) + { + // Immediate exit if key_quit is pressed in skip mode + I_SafeExit(0); + } + else + { + // key_use is used for seeing the current frame + if (ev->data1 != key_use && ev->data1 != key_demo_skip) + { + return; + } + } + } + } + + M_Responder(ev) || + (gamestate == GS_LEVEL && ( + HU_Responder(ev) || + ST_Responder(ev) || + AM_Responder(ev) + ) + ) || + G_Responder(ev); +} + +// +// D_Wipe +// +// CPhipps - moved the screen wipe code from D_Display to here +// The screens to wipe between are already stored, this just does the timing +// and screen updating + +static void D_Wipe(void) +{ + dboolean done; + int wipestart = I_GetTime () - 1; + + if (!render_wipescreen) return;//e6y + do + { + int nowtime, tics; + do + { + I_uSleep(5000); // CPhipps - don't thrash cpu in this loop + nowtime = I_GetTime(); + tics = nowtime - wipestart; + } + while (!tics); + wipestart = nowtime; + done = wipe_ScreenWipe(tics); + I_UpdateNoBlit(); + M_Drawer(); // menu is drawn even on top of wipes + I_FinishUpdate(); // page flip or blit buffer + if (capturing_video && !doSkip && cap_wipescreen) + { + I_CaptureFrame(); + } + } + while (!done); +} + +// +// D_Display +// draw current display, possibly wiping it from the previous +// + +// wipegamestate can be set to -1 to force a wipe on the next draw +gamestate_t wipegamestate = GS_DEMOSCREEN; +extern dboolean setsizeneeded; +extern int showMessages; + +void D_Display (fixed_t frac) +{ + static dboolean isborderstate = false; + static dboolean borderwillneedredraw = false; + static gamestate_t oldgamestate = -1; + dboolean wipe; + dboolean viewactive = false, isborder = false; + + // e6y + extern dboolean gamekeydown[]; + if (doSkip) + { + if (HU_DrawDemoProgress(false)) + I_FinishUpdate(); + if (!gamekeydown[key_use]) + return; + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + gld_PreprocessLevel(); + } +#endif + } + + if (!doSkip || !gamekeydown[key_use]) + + if (nodrawers) // for comparative timing / profiling + return; + + if (!I_StartDisplay()) + return; + + if (setsizeneeded) { // change the view size if needed + R_ExecuteSetViewSize(); + oldgamestate = -1; // force background redraw + } + + // save the current screen if about to wipe + if ((wipe = (gamestate != wipegamestate))) + { + wipe_StartScreen(); + R_ResetViewInterpolation(); + } + + if (gamestate != GS_LEVEL) { // Not a level + switch (oldgamestate) { + case -1: + case GS_LEVEL: + V_SetPalette(0); // cph - use default (basic) palette + default: + break; + } + + switch (gamestate) { + case GS_INTERMISSION: + WI_Drawer(); + break; + case GS_FINALE: + F_Drawer(); + break; + case GS_DEMOSCREEN: + D_PageDrawer(); + break; + default: + break; + } + } else if (gametic != basetic) { // In a level + dboolean redrawborderstuff; + + HU_Erase(); + + // Work out if the player view is visible, and if there is a border + viewactive = (!(automapmode & am_active) || (automapmode & am_overlay)) && !inhelpscreens; + isborder = viewactive ? (viewheight != SCREENHEIGHT) : (!inhelpscreens && (automapmode & am_active)); + + if (oldgamestate != GS_LEVEL) { + R_FillBackScreen (); // draw the pattern into the back screen + redrawborderstuff = isborder; + } else { + // CPhipps - + // If there is a border, and either there was no border last time, + // or the border might need refreshing, then redraw it. + redrawborderstuff = isborder && (!isborderstate || borderwillneedredraw); + // The border may need redrawing next time if the border surrounds the screen, + // and there is a menu being displayed + borderwillneedredraw = menuactive && isborder && viewactive; + // e6y + // I should do it because I call R_RenderPlayerView in all cases, + // not only if viewactive is true + borderwillneedredraw = (borderwillneedredraw) || + (((automapmode & am_active) && !(automapmode & am_overlay))); + } + if (redrawborderstuff || (V_GetMode() == VID_MODEGL)) + R_DrawViewBorder(); + + // e6y + // Boom colormaps should be applied for everything in R_RenderPlayerView + use_boom_cm=true; + + R_InterpolateView(&players[displayplayer], frac); + + R_ClearStats(); + + // Now do the drawing + if (viewactive || map_always_updates) + { + R_RenderPlayerView (&players[displayplayer]); + } + + // IDRATE cheat + R_ShowStats(); + + // e6y + // but should NOT be applied for automap, statusbar and HUD + use_boom_cm=false; + frame_fixedcolormap = 0; + + if (automapmode & am_active) + { + AM_Drawer(); + } + + R_RestoreInterpolations(); + + ST_Drawer( + ((viewheight != SCREENHEIGHT) + || ((automapmode & am_active) && !(automapmode & am_overlay))), + redrawborderstuff || BorderNeedRefresh, + (menuactive == mnact_full)); + + BorderNeedRefresh = false; + if (V_GetMode() != VID_MODEGL) + R_DrawViewBorder(); + HU_Drawer(); + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + gld_ProcessExtraAlpha(); +#endif + } + + isborderstate = isborder; + oldgamestate = wipegamestate = gamestate; + + // draw pause pic + if (paused && (menuactive != mnact_full)) { + // Simplified the "logic" here and no need for x-coord caching - POPE + V_DrawNamePatch((320 - V_NamePatchWidth("M_PAUSE"))/2, 4, + 0, "M_PAUSE", CR_DEFAULT, VPT_STRETCH); + } + + // menus go directly to the screen + M_Drawer(); // menu is drawn even on top of everything +#ifdef HAVE_NET + NetUpdate(); // send out any new accumulation +#else + D_BuildNewTiccmds(); +#endif + + HU_DrawDemoProgress(true); //e6y + + // normal update + if (!wipe) + I_FinishUpdate (); // page flip or blit buffer + else { + // wipe update + wipe_EndScreen(); + D_Wipe(); + } + + // e6y + // Don't thrash cpu during pausing or if the window doesnt have focus + if ( (paused && !walkcamera.type) || (!window_focused) ) { + I_uSleep(5000); + } + + I_EndDisplay(); +} + +// CPhipps - Auto screenshot Variables + +static int auto_shot_count, auto_shot_time; +static const char *auto_shot_fname; + +// +// D_DoomLoop() +// +// Not a globally visible function, +// just included for source reference, +// called by D_DoomMain, never exits. +// Manages timing and IO, +// calls all ?_Responder, ?_Ticker, and ?_Drawer, +// calls I_GetTime, I_StartFrame, and I_StartTic +// + +static void D_DoomLoop(void) +{ + if (quickstart_window_ms > 0) + I_uSleep(quickstart_window_ms * 1000); + + for (;;) + { + WasRenderedInTryRunTics = false; + // frame syncronous IO operations + I_StartFrame (); + + if (ffmap == gamemap) ffmap = 0; + + // process one or more tics + if (singletics) + { + I_StartTic (); + G_BuildTiccmd (&netcmds[consoleplayer][maketic%BACKUPTICS]); + if (advancedemo) + D_DoAdvanceDemo (); + M_Ticker (); + G_Ticker (); + P_Checksum(gametic); + gametic++; + maketic++; + } + else + TryRunTics (); // will run at least one tic + + // killough 3/16/98: change consoleplayer to displayplayer + if (players[displayplayer].mo) // cph 2002/08/10 + S_UpdateSounds(players[displayplayer].mo);// move positional sounds + + // Update display, next frame, with current state. + if (!movement_smooth || !WasRenderedInTryRunTics || gamestate != wipegamestate) + { + // NSM + if (capturing_video && !doSkip) + { + dboolean first = true; + int cap_step = TICRATE * FRACUNIT / cap_fps; + cap_frac += cap_step; + while(cap_frac <= FRACUNIT) + { + isExtraDDisplay = !first; + first = false; + D_Display(cap_frac); + isExtraDDisplay = false; + I_CaptureFrame(); + cap_frac += cap_step; + } + cap_frac -= FRACUNIT + cap_step; + } + else + { + D_Display(I_GetTimeFrac()); + } + } + + // CPhipps - auto screenshot + if (auto_shot_fname && !--auto_shot_count) { + auto_shot_count = auto_shot_time; + M_DoScreenShot(auto_shot_fname); + } +//e6y + if (avi_shot_fname && !doSkip) + { + int len; + char *avi_shot_curr_fname; + avi_shot_num++; + len = snprintf(NULL, 0, "%s%06d.tga", avi_shot_fname, avi_shot_num); + avi_shot_curr_fname = malloc(len+1); + sprintf(avi_shot_curr_fname, "%s%06d.tga", avi_shot_fname, avi_shot_num); + M_DoScreenShot(avi_shot_curr_fname); + free(avi_shot_curr_fname); + } +} +} + +// +// DEMO LOOP +// + +static int demosequence; // killough 5/2/98: made static +static int pagetic; +static const char *pagename; // CPhipps - const +dboolean bfgedition = 0; + +// +// D_PageTicker +// Handles timing for warped projection +// +void D_PageTicker(void) +{ + if (--pagetic < 0) + D_AdvanceDemo(); +} + +// +// D_PageDrawer +// +static void D_PageDrawer(void) +{ + // proff/nicolas 09/14/98 -- now stretchs bitmaps to fullscreen! + // CPhipps - updated for new patch drawing + // proff - added M_DrawCredits + if (pagename) + { + // e6y: wide-res + V_FillBorder(-1, 0); + V_DrawNamePatch(0, 0, 0, pagename, CR_DEFAULT, VPT_STRETCH); + } + else + M_DrawCredits(); +} + +// +// D_AdvanceDemo +// Called after each demo or intro demosequence finishes +// +void D_AdvanceDemo (void) +{ + advancedemo = true; +} + +/* killough 11/98: functions to perform demo sequences + * cphipps 10/99: constness fixes + */ + +static void D_SetPageName(const char *name) +{ + if ((bfgedition) && name && !strncmp(name,"TITLEPIC",8)) + pagename = "DMENUPIC"; + else + pagename = name; +} + +static void D_DrawTitle1(const char *name) +{ + S_StartMusic(mus_intro); + pagetic = (TICRATE*170)/35; + D_SetPageName(name); +} + +static void D_DrawTitle2(const char *name) +{ + S_StartMusic(mus_dm2ttl); + D_SetPageName(name); +} + +/* killough 11/98: tabulate demo sequences + */ + +static struct +{ + void (*func)(const char *); + const char *name; +} const demostates[][4] = + { + { + {D_DrawTitle1, "TITLEPIC"}, + {D_DrawTitle1, "TITLEPIC"}, + {D_DrawTitle2, "TITLEPIC"}, + {D_DrawTitle1, "TITLEPIC"}, + }, + + { + {G_DeferedPlayDemo, "demo1"}, + {G_DeferedPlayDemo, "demo1"}, + {G_DeferedPlayDemo, "demo1"}, + {G_DeferedPlayDemo, "demo1"}, + }, + { + {D_SetPageName, NULL}, + {D_SetPageName, NULL}, + {D_SetPageName, NULL}, + {D_SetPageName, NULL}, + }, + + { + {G_DeferedPlayDemo, "demo2"}, + {G_DeferedPlayDemo, "demo2"}, + {G_DeferedPlayDemo, "demo2"}, + {G_DeferedPlayDemo, "demo2"}, + }, + + { + {D_SetPageName, "HELP2"}, + {D_SetPageName, "HELP2"}, + {D_SetPageName, "CREDIT"}, + {D_DrawTitle1, "TITLEPIC"}, + }, + + { + {G_DeferedPlayDemo, "demo3"}, + {G_DeferedPlayDemo, "demo3"}, + {G_DeferedPlayDemo, "demo3"}, + {G_DeferedPlayDemo, "demo3"}, + }, + + { + {NULL}, + {NULL}, + // e6y + // Both Plutonia and TNT are commercial like Doom2, + // but in difference from Doom2, they have demo4 in demo cycle. + {G_DeferedPlayDemo, "demo4"}, + {D_SetPageName, "CREDIT"}, + }, + + { + {NULL}, + {NULL}, + {NULL}, + {G_DeferedPlayDemo, "demo4"}, + }, + + { + {NULL}, + {NULL}, + {NULL}, + {NULL}, + } + }; + +/* + * This cycles through the demo sequences. + * killough 11/98: made table-driven + */ + +void D_DoAdvanceDemo(void) +{ + players[consoleplayer].playerstate = PST_LIVE; /* not reborn */ + advancedemo = usergame = paused = false; + gameaction = ga_nothing; + + pagetic = TICRATE * 11; /* killough 11/98: default behavior */ + gamestate = GS_DEMOSCREEN; + + if (netgame && !demoplayback) { + demosequence = 0; + } else + if (!demostates[++demosequence][gamemode].func) + demosequence = 0; + // do not even attempt to play DEMO4 if it is not available + if (demosequence == 6 && gamemode == commercial && W_CheckNumForName("demo4") < 0) + demosequence = 0; + demostates[demosequence][gamemode].func + (demostates[demosequence][gamemode].name); +} + +// +// D_StartTitle +// +void D_StartTitle (void) +{ + gameaction = ga_nothing; + demosequence = -1; + D_AdvanceDemo(); +} + +// +// D_AddFile +// +// Rewritten by Lee Killough +// +// Ty 08/29/98 - add source parm to indicate where this came from +// CPhipps - static, const char* parameter +// - source is an enum +// - modified to allocate & use new wadfiles array +void D_AddFile (const char *file, wad_source_t source) +{ + char *gwa_filename=NULL; + int len; + + // There can only be one iwad source! + if (source == source_iwad) + { + int i; + + for (i = 0; i < numwadfiles; ++i) + if (wadfiles[i].src == source_iwad) + wadfiles[i].src = source_skip; + } + + wadfiles = realloc(wadfiles, sizeof(*wadfiles)*(numwadfiles+1)); + wadfiles[numwadfiles].name = + AddDefaultExtension(strcpy(malloc(strlen(file)+5), file), ".wad"); + wadfiles[numwadfiles].src = source; // Ty 08/29/98 + wadfiles[numwadfiles].handle = 0; + + // No Rest For The Living + len=strlen(wadfiles[numwadfiles].name); + if (len>=9 && !strnicmp(wadfiles[numwadfiles].name+len-9,"nerve.wad",9)) + gamemission = pack_nerve; + + numwadfiles++; + // proff: automatically try to add the gwa files + // proff - moved from w_wad.c + gwa_filename=AddDefaultExtension(strcpy(malloc(strlen(file)+5), file), ".wad"); + if (strlen(gwa_filename)>4) + if (!strcasecmp(gwa_filename+(strlen(gwa_filename)-4),".wad")) + { + char *ext; + ext = &gwa_filename[strlen(gwa_filename)-4]; + ext[1] = 'g'; ext[2] = 'w'; ext[3] = 'a'; + wadfiles = realloc(wadfiles, sizeof(*wadfiles)*(numwadfiles+1)); + wadfiles[numwadfiles].name = gwa_filename; + wadfiles[numwadfiles].src = source; // Ty 08/29/98 + wadfiles[numwadfiles].handle = 0; + numwadfiles++; + } +} + +// killough 10/98: support -dehout filename +// cph - made const, don't cache results +//e6y static +const char *D_dehout(void) +{ + int p = M_CheckParm("-dehout"); + if (!p) + p = M_CheckParm("-bexout"); + return (p && ++p < myargc ? myargv[p] : NULL); +} + +// +// CheckIWAD +// +// Verify a file is indeed tagged as an IWAD +// Scan its lumps for levelnames and return gamemode as indicated +// Detect missing wolf levels in DOOM II +// +// The filename to check is passed in iwadname, the gamemode detected is +// returned in gmode, hassec returns the presence of secret levels +// +// jff 4/19/98 Add routine to test IWAD for validity and determine +// the gamemode from it. Also note if DOOM II, whether secret levels exist +// CPhipps - const char* for iwadname, made static +//e6y static +void CheckIWAD(const char *iwadname,GameMode_t *gmode,dboolean *hassec) +{ + if ( !M_access (iwadname,R_OK) ) + { + int ud=0,rg=0,sw=0,cm=0,sc=0,hx=0; + FILE* fp; + + // Identify IWAD correctly + if ((fp = M_fopen(iwadname, "rb"))) + { + wadinfo_t header; + + // read IWAD header + if (fread(&header, sizeof(header), 1, fp) == 1) + { + size_t length; + filelump_t *fileinfo; + + if (strncmp(header.identification, "IWAD", 4)) // missing IWAD tag in header + { + lprintf(LO_WARN,"CheckIWAD: IWAD tag %s not present\n", iwadname); + } + + // read IWAD directory + header.numlumps = LittleLong(header.numlumps); + header.infotableofs = LittleLong(header.infotableofs); + length = header.numlumps; + fileinfo = malloc(length*sizeof(filelump_t)); + if (fseek (fp, header.infotableofs, SEEK_SET) || + fread (fileinfo, sizeof(filelump_t), length, fp) != length) + { + fclose(fp); + I_Error("CheckIWAD: failed to read directory %s",iwadname); + } + + // scan directory for levelname lumps + while (length--) + { + if (fileinfo[length].name[0] == 'E' && + fileinfo[length].name[2] == 'M' && + fileinfo[length].name[4] == 0) + { + if (fileinfo[length].name[1] == '4') + ++ud; + else if (fileinfo[length].name[1] == '3') + ++rg; + else if (fileinfo[length].name[1] == '2') + ++rg; + else if (fileinfo[length].name[1] == '1') + ++sw; + } + else if (fileinfo[length].name[0] == 'M' && + fileinfo[length].name[1] == 'A' && + fileinfo[length].name[2] == 'P' && + fileinfo[length].name[5] == 0) + { + ++cm; + if (fileinfo[length].name[3] == '3') + if (fileinfo[length].name[4] == '1' || + fileinfo[length].name[4] == '2') + ++sc; + } + + if (!strncmp(fileinfo[length].name,"DMENUPIC",8)) + bfgedition++; + if (!strncmp(fileinfo[length].name,"HACX",4)) + hx++; + } + free(fileinfo); + + } + + fclose(fp); + } + else // error from open call + I_Error("CheckIWAD: Can't open IWAD %s", iwadname); + + // Determine game mode from levels present + // Must be a full set for whichever mode is present + // Lack of wolf-3d levels also detected here + + *gmode = indetermined; + *hassec = false; + if (cm>=30 || (cm>=20 && hx)) + { + *gmode = commercial; + *hassec = sc>=2; + } + else if (ud>=9) + *gmode = retail; + else if (rg>=18) + *gmode = registered; + else if (sw>=9) + *gmode = shareware; + } + else // error from access call + I_Error("CheckIWAD: IWAD %s not readable", iwadname); +} + +// +// AddIWAD +// +void AddIWAD(const char *iwad) +{ + size_t i; + + if (!(iwad && *iwad)) + return; + + //jff 9/3/98 use logical output routine + lprintf(LO_CONFIRM,"IWAD found: %s\n",iwad); //jff 4/20/98 print only if found + CheckIWAD(iwad,&gamemode,&haswolflevels); + + /* jff 8/23/98 set gamemission global appropriately in all cases + * cphipps 12/1999 - no version output here, leave that to the caller + */ + i = strlen(iwad); + switch(gamemode) + { + case retail: + case registered: + case shareware: + gamemission = doom; + if (i>=8 && !strnicmp(iwad+i-8,"chex.wad",8)) + gamemission = chex; + break; + case commercial: + gamemission = doom2; + if (i>=10 && !strnicmp(iwad+i-10,"doom2f.wad",10)) + language=french; + else if (i>=7 && !strnicmp(iwad+i-7,"tnt.wad",7)) + gamemission = pack_tnt; + else if (i>=12 && !strnicmp(iwad+i-12,"plutonia.wad",12)) + gamemission = pack_plut; + else if (i>=8 && !strnicmp(iwad+i-8,"hacx.wad",8)) + gamemission = hacx; + break; + default: + gamemission = none; + break; + } + if (gamemode == indetermined) + //jff 9/3/98 use logical output routine + lprintf(LO_WARN,"Unknown Game Version, may not work\n"); + D_AddFile(iwad,source_iwad); +} + +// NormalizeSlashes +// +// Remove trailing slashes, translate backslashes to slashes +// The string to normalize is passed and returned in str +// +// jff 4/19/98 Make killoughs slash fixer a subroutine +// +static void NormalizeSlashes(char *str) +{ + size_t l; + + // killough 1/18/98: Neater / \ handling. + // Remove trailing / or \ to prevent // /\ \/ \\, and change \ to / + + if (!str || !(l = strlen(str))) + return; + if (str[--l]=='/' || str[l]=='\\') // killough 1/18/98 + str[l]=0; + while (l--) + if (str[l]=='\\') + str[l]='/'; +} + +/* + * FindIWADFIle + * + * Search for one of the standard IWADs + * CPhipps - static, proper prototype + * - 12/1999 - rewritten to use I_FindFile + */ +static char *FindIWADFile(void) +{ + int i; + char * iwad = NULL; + + i = M_CheckParm("-iwad"); + if (i && (++i < myargc)) { + iwad = I_FindFile(myargv[i], ".wad"); + } else { + for (i=0; !iwad && i 0 && isspace(*infile)) { infile++; size--; } + if (size > 0) { + char *s = malloc(size+1); + char *p = s; + int quoted = 0; + + while (size > 0) { + // Whitespace terminates the token unless quoted + if (!quoted && isspace(*infile)) break; + if (*infile == '\"') { + // Quotes are removed but remembered + infile++; size--; quoted ^= 1; + } else { + *p++ = *infile++; size--; + } + } + if (quoted) I_Error("Runaway quoted string in response file"); + + // Terminate string, realloc and add to argv + *p = 0; + newargv = realloc(newargv, sizeof(newargv[0]) * (indexinfile + 1)); + newargv[indexinfile++] = realloc(s,strlen(s)+1); + } + } while(size > 0); + } + free(file); + + newargv = realloc(newargv, sizeof(newargv[0]) * (indexinfile + index)); + memcpy((void *)&newargv[indexinfile],moreargs,index*sizeof(moreargs[0])); + free((void *)moreargs); + + myargc = indexinfile+index; + myargv = newargv; + + // DISPLAY ARGS + //jff 9/3/98 use logical output routine + lprintf(LO_CONFIRM,"%d command-line args:\n",myargc); + for (index=1;index= extlen && !stricmp(&myargv[i][arglen - extlen], looses[k].ext)) + { + (*(looses[k].list))[(*looses[k].count)++] = strdup(myargv[i]); + break; + } + k++; + } + /*if (myargv[i][j-4] != '.') // assume wad if no extension + wads[wadcount++] = strdup(myargv[i]);*/ + skip[i] = true; // nuke that entry so it won't repeat later + } + + // Now, if we didn't find any loose files, we can just leave. + if (wadcount+lmpcount+dehcount != 0) + { + n = 0; + k = 0; + while (params[k].cmdparam) + { + if ((p = M_CheckParm (params[k].cmdparam))) + { + skip[p] = true; // nuke the entry + while (++p != myargc && *myargv[p] != '-') + { + (*(params[k].list))[(*params[k].count)++] = strdup(myargv[p]); + skip[p] = true; // null any we find and save + } + } + else + { + if (*(params[k].count) > 0) + { + n++; + } + } + k++; + } + + // Now go back and redo the whole myargv array with our stuff in it. + // First, create a new myargv array to copy into + tmyargv = calloc(sizeof(tmyargv[0]), myargc + n); + tmyargv[0] = myargv[0]; // invocation + tmyargc = 1; + + k = 0; + while (params[k].cmdparam) + { + // put our stuff into it + if (*(params[k].count) > 0) + { + tmyargv[tmyargc++] = strdup(params[k].cmdparam); // put the switch in + for (i=0;i<*(params[k].count);) + tmyargv[tmyargc++] = (*(params[k].list))[i++]; // allocated by strdup above + } + k++; + } + + // then copy everything that's there now + for (i = 1; i < myargc; i++) + { + if (!skip[i]) // skip any zapped entries + tmyargv[tmyargc++] = myargv[i]; // pointers are still valid + } + // now make the global variables point to our array + myargv = tmyargv; + myargc = tmyargc; + } + + free(wads); + free(lmps); + free(dehs); + free(skip); +} + +/* cph - MBF-like wad/deh/bex autoload code */ +const char *wad_files[MAXLOADFILES], *deh_files[MAXLOADFILES]; + +// CPhipps - misc screen stuff +int desired_screenwidth, desired_screenheight; + +static void L_SetupConsoleMasks(void) { + int p; + int i; + const char *cena="ICWEFDA",*pos; //jff 9/3/98 use this for parsing console masks // CPhipps - const char*'s + + //jff 9/3/98 get mask for console output filter + if ((p = M_CheckParm ("-cout"))) { + lprintf(LO_DEBUG, "mask for stdout console output: "); + if (++p != myargc && *myargv[p] != '-') + for (i=0,cons_output_mask=0;(size_t)i filename && *basename != '/' && *basename != '\\') + basename--; + if (*basename == '/' || *basename == '\\') + basename++; + + return basename; +} + +const char *IWADBaseName(void) +{ + int i; + + for (i = 0; i < numwadfiles; i++) + { + if (wadfiles[i].src == source_iwad) + break; + } + + if (i == numwadfiles) + I_Error("IWADBaseName: IWAD not found\n"); + + return BaseName(wadfiles[i].name); +} + +// Load all WAD files from the given directory. + +static void AutoLoadWADs(const char *path) +{ + glob_t *glob; + const char *filename; + + glob = I_StartMultiGlob(path, GLOB_FLAG_NOCASE|GLOB_FLAG_SORTED, + "*.wad", "*.lmp", NULL); + for (;;) + { + filename = I_NextGlob(glob); + if (filename == NULL) + { + break; + } + D_AddFile(filename,source_auto_load); + } + + I_EndGlob(glob); +} + +// auto-loading of .wad files. + +void D_AutoloadIWadDir() +{ + int iter; + char *base; + + for (iter = 0; (base = GetAutoloadBaseDir(iter)); iter++) + { + char *autoload_dir; + + // common auto-loaded files for all Doom flavors + autoload_dir = GetAutoloadDir(base, "doom-all", true); + AutoLoadWADs(autoload_dir); + free(autoload_dir); + + // auto-loaded files per IWAD + autoload_dir = GetAutoloadDir(base, IWADBaseName(), true); + AutoLoadWADs(autoload_dir); + free(autoload_dir); + } +} + +static void D_AutoloadPWadDir() +{ + int iter; + char *base; + + for (iter = 0; (base = GetAutoloadBaseDir(iter)); iter++) + { + int i; + for (i = 0; i < numwadfiles; ++i) + if (wadfiles[i].src == source_pwad) + { + char *autoload_dir; + autoload_dir = GetAutoloadDir(base, BaseName(wadfiles[i].name), false); + AutoLoadWADs(autoload_dir); + free(autoload_dir); + } + } +} + +// Load all dehacked patches from the given directory. + +static void AutoLoadPatches(const char *path) +{ + const char *filename; + glob_t *glob; + + glob = I_StartMultiGlob(path, GLOB_FLAG_NOCASE|GLOB_FLAG_SORTED, + "*.deh", "*.bex", NULL); + for (;;) + { + filename = I_NextGlob(glob); + if (filename == NULL) + { + break; + } + ProcessDehFile(filename, D_dehout(), 0); + } + + I_EndGlob(glob); +} + +// auto-loading of .deh files. + +static void D_AutoloadDehDir() +{ + int iter; + char *base; + + for (iter = 0; (base = GetAutoloadBaseDir(iter)); iter++) + { + char *autoload_dir; + + // common auto-loaded files for all Doom flavors + autoload_dir = GetAutoloadDir(base, "doom-all", true); + AutoLoadPatches(autoload_dir); + free(autoload_dir); + + // auto-loaded files per IWAD + autoload_dir = GetAutoloadDir(base, IWADBaseName(), true); + AutoLoadPatches(autoload_dir); + free(autoload_dir); + } +} + +static void D_AutoloadDehPWadDir() +{ + int iter; + char *base; + + for (iter = 0; (base = GetAutoloadBaseDir(iter)); iter++) + { + int i; + for (i = 0; i < numwadfiles; ++i) + if (wadfiles[i].src == source_pwad) + { + char *autoload_dir; + autoload_dir = GetAutoloadDir(base, BaseName(wadfiles[i].name), false); + AutoLoadPatches(autoload_dir); + free(autoload_dir); + } + } +} + +// +// D_DoomMainSetup +// +// CPhipps - the old contents of D_DoomMain, but moved out of the main +// line of execution so its stack space can be freed +const char* doomverstr = NULL; + +int warpepisode = -1, warpmap = -1; + +static void D_DoomMainSetup(void) +{ + int p,slot; + + L_SetupConsoleMasks(); + + setbuf(stdout,NULL); + + // proff 04/05/2000: Added support for include response files + /* proff 2001/7/1 - Moved up, so -config can be in response files */ + { + dboolean rsp_found; + int i; + + do { + rsp_found=false; + for (i=0; i 400) + scale = 400; + //jff 9/3/98 use logical output routine + lprintf (LO_CONFIRM,"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; + } + + modifiedgame = false; + + // get skill / episode / map from parms + + startskill = sk_none; // jff 3/24/98 was sk_medium, just note not picked + startepisode = 1; + startmap = 1; + autostart = false; + + if ((p = M_CheckParm ("-skill")) && p < myargc-1) + { + startskill = myargv[p+1][0]-'1'; + autostart = true; + } + + if ((p = M_CheckParm ("-episode")) && p < myargc-1) + { + startepisode = myargv[p+1][0]-'0'; + startmap = 1; + autostart = true; + } + + if ((p = M_CheckParm ("-timer")) && p < myargc-1 && deathmatch) + { + int time = atoi(myargv[p+1]); + //jff 9/3/98 use logical output routine + lprintf(LO_CONFIRM,"Levels will end after %d minute%s.\n", time, time>1 ? "s" : ""); + } + + if ((p = M_CheckParm ("-avg")) && p < myargc-1 && deathmatch) + //jff 9/3/98 use logical output routine + lprintf(LO_CONFIRM,"Austin Virtual Gaming: Levels will end after 20 minutes\n"); + + if ((p = M_CheckParm ("-warp")) || // killough 5/2/98 + (p = M_CheckParm ("-wart"))) + // Ty 08/29/98 - moved this check later so we can have -warp alone: && p < myargc-1) + { + startmap = -1; // Ty 08/29/98 - allow "-warp x" to go to first map in wad(s) + autostart = true; // Ty 08/29/98 - move outside the decision tree + if (gamemode == commercial) + { + if (p < myargc-1) + { + int map; + if (sscanf(myargv[p+1], "%d", &map) == 1) + { + startmap = map; + } + warpmap = startmap; + } + } + else // 1/25/98 killough: fix -warp xxx from crashing Doom 1 / UD + { + if (p < myargc-1) + { + int episode, map; + if (sscanf(myargv[p+1], "%d", &episode) == 1) + { + startepisode = episode; + startmap = 1; + if (p < myargc-2 && sscanf(myargv[p+2], "%d", &map) == 1) + { + startmap = map; + } + warpepisode = startepisode; + warpmap = startmap; + } + } + } + } + // Ty 08/29/98 - later we'll check for startmap=-1 and autostart=true + // as a special case that -warp * was used. Actually -warp with any + // non-numeric will do that but we'll only document "*" + + //jff 1/22/98 add command line parms to disable sound and music + { + int nosound = M_CheckParm("-nosound"); + nomusicparm = nosound || M_CheckParm("-nomusic"); + nosfxparm = nosound || M_CheckParm("-nosfx"); + } + //jff end of sound/music command line parms + + // killough 3/2/98: allow -nodraw -noblit generally + nodrawers = M_CheckParm ("-nodraw"); + noblit = M_CheckParm ("-noblit"); + + //proff 11/22/98: Added setting of viewangleoffset + p = M_CheckParm("-viewangle"); + if (p && p < myargc-1) + { + viewangleoffset = atoi(myargv[p+1]); + viewangleoffset = viewangleoffset<0 ? 0 : (viewangleoffset>7 ? 7 : viewangleoffset); + viewangleoffset = (8-viewangleoffset) * ANG45; + } + + // init subsystems + + G_ReloadDefaults(); // killough 3/4/98: set defaults just loaded. + // jff 3/24/98 this sets startskill if it was -1 + +#ifdef GL_DOOM + // proff 04/05/2000: for GL-specific switches + gld_InitCommandLine(); +#endif + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"V_Init: allocate screens.\n"); + V_Init(); + + //e6y: Calculate the screen resolution and init all buffers + I_InitScreenResolution(); + + //e6y: some stuff from command-line should be initialised before ProcessDehFile() + e6y_InitCommandLine(); + + // CPhipps - autoloading of wads + // Designed to be general, instead of specific to boomlump.wad + // Some people might find this useful + // cph - support MBF -noload parameter + { + // only autoloaded wads here - autoloaded patches moved down below W_Init + int i, imax = MAXLOADFILES; + + // make sure to always autoload prboom-plus.wad + if (M_CheckParm("-noload")) + imax = 1; + + for (i=0; i= myargc-1) { /* killough */ + if ((p = M_CheckParm ("-fastdemo")) && p < myargc-1) /* killough */ + fastdemo = true; // run at fastest speed possible + else + { + if ((p = IsDemoContinue())) + { + democontinue = true; + AddDefaultExtension(strcpy(democontinuename, myargv[p + 2]), ".lmp"); + } + else + { + p = M_CheckParm ("-timedemo"); + } + } + } + + if (p && p < myargc-1) + { + char *file = malloc(strlen(myargv[p+1])+4+1); // cph - localised + strcpy(file,myargv[p+1]); + AddDefaultExtension(file,".lmp"); // killough + D_AddFile (file,source_lmp); + //jff 9/3/98 use logical output routine + lprintf(LO_CONFIRM,"Playing demo %s\n",file); + if ((p = M_CheckParm ("-ffmap")) && p < myargc-1) { + ffmap = atoi(myargv[p+1]); + } + free(file); + } + + // internal translucency set to config file value // phares + general_translucency = default_translucency; // phares + + //e6y + { + int demo_footer = CheckDemoExDemo(); + if (!demo_footer) + demo_footer = CheckAutoDemo(); +#ifdef USE_WINDOWS_LAUNCHER + LauncherShow(demo_footer); +#endif + } + + // add wad files from autoload PWAD directories + + D_AutoloadPWadDir(); + + + // 1/18/98 killough: Z_Init() call moved to i_main.c + + // CPhipps - move up netgame init + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"D_InitNetGame: Checking for network game.\n"); + D_InitNetGame(); + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"W_Init: Init WADfiles.\n"); + W_Init(); // CPhipps - handling of wadfiles init changed + + lprintf(LO_INFO,"\n"); // killough 3/6/98: add a newline, by popular demand :) + + // e6y + // option to disable automatic loading of dehacked-in-wad lump + if (!M_CheckParm ("-nodeh")) + { + // MBF-style DeHackEd in wad support: load all lumps, not just the last one + for (p = -1; (p = W_ListNumFromName("DEHACKED", p)) >= 0; ) + // Split loading DEHACKED lumps into IWAD/autoload and PWADs/others + if (lumpinfo[p].source == source_iwad + || lumpinfo[p].source == source_pre + || lumpinfo[p].source == source_auto_load) + ProcessDehFile(NULL, D_dehout(), p); // cph - add dehacked-in-a-wad support + + if (bfgedition) + { + int lump = (W_CheckNumForName)("BFGBEX", ns_prboom); + if (lump != -1) + { + ProcessDehFile(NULL, D_dehout(), lump); + } + } + if (gamemission == pack_nerve) + { + int lump = (W_CheckNumForName)("NERVEBEX", ns_prboom); + if (lump != -1) + { + ProcessDehFile(NULL, D_dehout(), lump); + } + } + if (gamemission == chex) + { + int lump = (W_CheckNumForName)("CHEXDEH", ns_prboom); + if (lump != -1) + { + ProcessDehFile(NULL, D_dehout(), lump); + } + } + } + + // Automatic pistol start when advancing from one level to the next. At the + // beginning of each level, the player's health is reset to 100, their + // armor to 0 and their inventory is reduced to the following: pistol, + // fists and 50 bullets. + + pistolstart = M_CheckParm("-pistolstart"); + + if (!M_CheckParm("-noload")) { + // now do autoloaded dehacked patches, after IWAD patches but before PWAD + int i; + + for (i=0; i= 0; ) + if (!(lumpinfo[p].source == source_iwad + || lumpinfo[p].source == source_pre + || lumpinfo[p].source == source_auto_load)) + ProcessDehFile(NULL, D_dehout(), p); + + // process .deh files from PWADs autoload directories + + D_AutoloadDehPWadDir(); + + // Load command line dehacked patches after WAD dehacked patches + + // e6y: DEH files preloaded in wrong order + // http://sourceforge.net/tracker/index.php?func=detail&aid=1418158&group_id=148658&atid=772943 + + // ty 03/09/98 do dehacked stuff + // Using -deh in BOOM, others use -dehacked. + // Ty 03/18/98 also allow .bex extension. .bex overrides if both exist. + + p = M_CheckParm ("-deh"); + if (p) + { + // the parms after p are deh/bex file names, + // until end of parms or another - preceded parm + // Ty 04/11/98 - Allow multiple -deh files in a row + // + // e6y + // reorganization of the code for looking for bex/deh patches + // in all standard dirs (%DOOMWADDIR%, etc) + while (++p != myargc && *myargv[p] != '-') + { + char *file = NULL; + if ((file = I_FindFile(myargv[p], ".bex")) || + (file = I_FindFile(myargv[p], ".deh"))) + { + // during the beta we have debug output to dehout.txt + ProcessDehFile(file,D_dehout(),0); + free(file); + } + else + { + I_Error("D_DoomMainSetup: Cannot find .deh or .bex file named %s",myargv[p]); + } + } + } + + if (!M_CheckParm("-nomapinfo")) + { + int p; + for (p = -1; (p = W_ListNumFromName("UMAPINFO", p)) >= 0; ) + { + const unsigned char * lump = (const unsigned char *)W_CacheLumpNum(p); + ParseUMapInfo(lump, W_LumpLength(p), I_Error); + umapinfo_loaded = true; + } + } + + + V_InitColorTranslation(); //jff 4/24/98 load color translation lumps + + // killough 2/22/98: copyright / "modified game" / SPA banners removed + + // Ty 04/08/98 - Add 5 lines of misc. data, only if nonblank + // The expectation is that these will be set in a .bex file + //jff 9/3/98 use logical output routine + if (*startup1) lprintf(LO_INFO,"%s",startup1); + if (*startup2) lprintf(LO_INFO,"%s",startup2); + if (*startup3) lprintf(LO_INFO,"%s",startup3); + if (*startup4) lprintf(LO_INFO,"%s",startup4); + if (*startup5) lprintf(LO_INFO,"%s",startup5); + // End new startup strings + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"M_Init: Init miscellaneous info.\n"); + M_Init(); + +#ifdef HAVE_NET + // CPhipps - now wait for netgame start + D_CheckNetGame(); +#endif + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"R_Init: Init DOOM refresh daemon - "); + R_Init(); + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"\nP_Init: Init Playloop state.\n"); + P_Init(); + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"I_Init: Setting up machine state.\n"); + I_Init(); + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"S_Init: Setting up sound.\n"); + S_Init(snd_SfxVolume /* *8 */, snd_MusicVolume /* *8*/ ); + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"HU_Init: Setting up heads up display.\n"); + HU_Init(); + + if (!(M_CheckParm("-nodraw") && M_CheckParm("-nosound"))) + I_InitGraphics(); + + // NSM + if ((p = M_CheckParm("-viddump")) && (p < myargc-1)) + { + I_CapturePrep(myargv[p + 1]); + } + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"ST_Init: Init status bar.\n"); + ST_Init(); + + // CPhipps - auto screenshots + if ((p = M_CheckParm("-autoshot")) && (p < myargc-2)) + if ((auto_shot_count = auto_shot_time = atoi(myargv[p+1]))) + auto_shot_fname = myargv[p+2]; + + if ((p = M_CheckParm("-statdump")) && (p < myargc-1)) + { + I_AtExit(StatDump, true); + lprintf(LO_INFO,"External statistics registered.\n"); + } + + // start the apropriate game based on parms + + // killough 12/98: + // Support -loadgame with -record and reimplement -recordfrom. + + if ((slot = M_CheckParm("-recordfrom")) && (p = slot+2) < myargc) + G_RecordDemo(myargv[p]); + else + { + slot = M_CheckParm("-loadgame"); + if ((p = M_CheckParm("-record")) && ++p < myargc) + { + autostart = true; + G_RecordDemo(myargv[p]); + } + } + + if ((p = M_CheckParm ("-checksum")) && ++p < myargc) + { + P_RecordChecksum (myargv[p]); + } + + if ((p = M_CheckParm ("-fastdemo")) && ++p < myargc) + { // killough + fastdemo = true; // run at fastest speed possible + timingdemo = true; // show stats after quit + G_DeferedPlayDemo(myargv[p]); + singledemo = true; // quit after one demo + } + else + if ((p = M_CheckParm("-timedemo")) && ++p < myargc) + { + singletics = true; + timingdemo = true; // show stats after quit + G_DeferedPlayDemo(myargv[p]); + singledemo = true; // quit after one demo + } + else + if ((p = M_CheckParm("-playdemo")) && ++p < myargc) + { + G_DeferedPlayDemo(myargv[p]); + singledemo = true; // quit after one demo + } + else + //e6y + if ((p = IsDemoContinue())) + { + G_DeferedPlayDemo(myargv[p+1]); + G_CheckDemoContinue(); + } + + if (slot && ++slot < myargc) + { + slot = atoi(myargv[slot]); // killough 3/16/98: add slot info + G_LoadGame(slot, true); // killough 5/15/98: add command flag // cph - no filename + } + else + if (!singledemo) { /* killough 12/98 */ + if (autostart || netgame) + { + // sets first map and first episode if unknown + if (autostart) + { + GetFirstMap(&startepisode, &startmap); + } + G_InitNew(startskill, startepisode, startmap); + if (demorecording) + G_BeginRecording(); + } + else + D_StartTitle(); // start up intro loop + } + + // do not try to interpolate during timedemo + M_ChangeUncappedFrameRate(); +} + +// +// D_DoomMain +// + +void D_DoomMain(void) +{ + D_DoomMainSetup(); // CPhipps - setup out of main execution stack + + D_DoomLoop (); // never returns +} + +// +// GetFirstMap +// +// Ty 08/29/98 - determine first available map from the loaded wads and run it +// + +void GetFirstMap(int *ep, int *map) +{ + int i,j; // used to generate map name + dboolean done = false; // Ty 09/13/98 - to exit inner loops + char test[6]; // MAPxx or ExMx plus terminator for testing + char name[6]; // MAPxx or ExMx plus terminator for display + dboolean newlevel = false; // Ty 10/04/98 - to test for new level + int ix; // index for lookup + + strcpy(name,""); // initialize + if (*map == -1) // unknown so go search for first changed one + { + *ep = 1; + *map = 1; // default E1M1 or MAP01 + if (gamemode == commercial) + { + for (i=1;!done && i<33;i++) // Ty 09/13/98 - add use of !done + { + snprintf(test,sizeof(test),"MAP%02d",i); + ix = W_CheckNumForName(test); + if (ix != -1) // Ty 10/04/98 avoid -1 subscript + { + if (lumpinfo[ix].source == source_pwad) + { + *map = i; + strcpy(name,test); // Ty 10/04/98 + done = true; // Ty 09/13/98 + newlevel = true; // Ty 10/04/98 + } + else + { + if (!*name) // found one, not pwad. First default. + strcpy(name,test); + } + } + } + } + else // one of the others + { + strcpy(name,"E1M1"); // Ty 10/04/98 - default for display + for (i=1;!done && i<5;i++) // Ty 09/13/98 - add use of !done + { + for (j=1;!done && j<10;j++) // Ty 09/13/98 - add use of !done + { + snprintf(test,sizeof(test),"E%dM%d",i,j); + ix = W_CheckNumForName(test); + if (ix != -1) // Ty 10/04/98 avoid -1 subscript + { + if (lumpinfo[ix].source == source_pwad) + { + *ep = i; + *map = j; + strcpy(name,test); // Ty 10/04/98 + done = true; // Ty 09/13/98 + newlevel = true; // Ty 10/04/98 + } + else + { + if (!*name) // found one, not pwad. First default. + strcpy(name,test); + } + } + } + } + } + //jff 9/3/98 use logical output routine + lprintf(LO_CONFIRM,"Auto-warping to first %slevel: %s\n", + newlevel ? "new " : "", name); // Ty 10/04/98 - new level test + } +} diff --git a/src/d_main.h b/src/d_main.h new file mode 100644 index 0000000..3d675b5 --- /dev/null +++ b/src/d_main.h @@ -0,0 +1,87 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Main startup and splash screenstuff. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __D_MAIN__ +#define __D_MAIN__ + +#include "m_fixed.h" +#include "d_event.h" +#include "w_wad.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +/* CPhipps - removed wadfiles[] stuff to w_wad.h */ + +extern char *basesavegame; // killough 2/16/98: savegame path + +//jff 1/24/98 make command line copies of play modes available +extern dboolean clnomonsters; // checkparm of -nomonsters +extern dboolean clrespawnparm; // checkparm of -respawn +extern dboolean clfastparm; // checkparm of -fast +//jff end of external declaration of command line playmode + +extern dboolean nosfxparm; +extern dboolean nomusicparm; +extern dboolean umapinfo_loaded; +extern int ffmap; + +// Called by IO functions when input is detected. +void D_PostEvent(event_t* ev); + +// Demo stuff +extern dboolean advancedemo; +void D_AdvanceDemo(void); +void D_DoAdvanceDemo (void); + +// +// BASE LEVEL +// + +void D_Display(fixed_t frac); +void D_PageTicker(void); +void D_StartTitle(void); +void D_DoomMain(void); +void D_AddFile (const char *file, wad_source_t source); + +void AddIWAD(const char *iwad); +const char *BaseName(const char *filename); + +/* cph - MBF-like wad/deh/bex autoload code */ +/* proff 2001/7/1 - added prboom.wad as last entry so it's always loaded and + doesn't overlap with the cfg settings */ +#define MAXLOADFILES 3 +extern const char *wad_files[MAXLOADFILES], *deh_files[MAXLOADFILES]; + +#endif diff --git a/src/d_net.h b/src/d_net.h new file mode 100644 index 0000000..319f55b --- /dev/null +++ b/src/d_net.h @@ -0,0 +1,214 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Networking stuff. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __D_NET__ +#define __D_NET__ + +#include "d_player.h" + + +#ifdef __GNUG__ +#pragma interface +#endif + + +// +// Network play related stuff. +// There is a data struct that stores network +// communication related stuff, and another +// one that defines the actual packets to +// be transmitted. +// + +#define DOOMCOM_ID 0x12345678l + +// Max computers/players in a game. +#define MAXNETNODES 8 + + +typedef enum +{ + CMD_SEND = 1, + CMD_GET = 2 + +} command_t; + + +// +// Network packet data. +// +typedef struct +{ + // High bit is retransmit request. + unsigned checksum; + // Only valid if NCMD_RETRANSMIT. + byte retransmitfrom; + + byte starttic; + byte player; + byte numtics; + ticcmd_t cmds[BACKUPTICS]; + +} doomdata_t; + +// +// Startup packet difference +// SG: 4/12/98 +// Added so we can send more startup data to synch things like +// bobbing, recoil, etc. +// this is just mapped over the ticcmd_t array when setup packet is sent +// +// Note: the original code takes care of startskill, deathmatch, nomonsters +// respawn, startepisode, startmap +// Note: for phase 1 we need to add monsters_remember, variable_friction, +// weapon_recoil, allow_pushers, over_under, player_bobbing, +// fastparm, demo_insurance, and the rngseed +//Stick all options into bytes so we don't need to mess with bitfields +//WARNING: make sure this doesn't exceed the size of the ticcmds area! +//sizeof(ticcmd_t)*BACKUPTICS +//This is the current length of our extra stuff +// +//killough 5/2/98: this should all be replaced by calls to G_WriteOptions() +//and G_ReadOptions(), which were specifically designed to set up packets. +//By creating a separate struct and functions to read/write the options, +//you now have two functions and data to maintain instead of just one. +//If the array in g_game.c which G_WriteOptions()/G_ReadOptions() operates +//on, is too large (more than sizeof(ticcmd_t)*BACKUPTICS), it can +//either be shortened, or the net code needs to divide it up +//automatically into packets. The STARTUPLEN below is non-portable. +//There's a portable way to do it without having to know the sizes. + +#define STARTUPLEN 12 +typedef struct +{ + byte monsters_remember; + byte variable_friction; + byte weapon_recoil; + byte allow_pushers; + byte over_under; + byte player_bobbing; + byte fastparm; + byte demo_insurance; + unsigned int rngseed; + char filler[sizeof(ticcmd_t)*BACKUPTICS-STARTUPLEN]; +} startup_t; + +typedef enum { + // Leave space, so low values corresponding to normal netgame setup packets can be ignored + nm_plcolour = 3, + nm_savegamename = 4, +} netmisctype_t; + +typedef struct +{ + netmisctype_t type; + size_t len; + byte value[sizeof(ticcmd_t)*BACKUPTICS - sizeof(netmisctype_t) - sizeof(size_t)]; +} netmisc_t; + +typedef struct +{ + // Supposed to be DOOMCOM_ID? + long id; + + // DOOM executes an int to execute commands. + short intnum; + // Communication between DOOM and the driver. + // Is CMD_SEND or CMD_GET. + short command; + // Is dest for send, set by get (-1 = no packet). + short remotenode; + + // Number of bytes in doomdata to be sent + short datalength; + + // Info common to all nodes. + // Console is allways node 0. + short numnodes; + // Flag: 1 = no duplication, 2-5 = dup for slow nets. + short ticdup; + // Flag: 1 = send a backup tic in every packet. + short extratics; + // Flag: 1 = deathmatch. + short deathmatch; + // Flag: -1 = new game, 0-5 = load savegame + short savegame; + short episode; // 1-3 + short map; // 1-9 + short skill; // 1-5 + + // Info specific to this node. + short consoleplayer; + short numplayers; + + // These are related to the 3-display mode, + // in which two drones looking left and right + // were used to render two additional views + // on two additional computers. + // Probably not operational anymore. + // 1 = left, 0 = center, -1 = right + short angleoffset; + // 1 = drone + short drone; + + // The packet data to be sent. + doomdata_t data; + +} doomcom_t; + +// Create any new ticcmds and broadcast to other players. +#ifdef HAVE_NET +void NetUpdate (void); +#else +void D_BuildNewTiccmds(void); +#endif + +//? how many ticks to run? +void TryRunTics (void); + +// CPhipps - move to header file +void D_InitNetGame (void); // This does the setup +void D_CheckNetGame(void); // This waits for game start + +// CPhipps - misc info broadcast +void D_NetSendMisc(netmisctype_t type, size_t len, void* data); + +// CPhipps - ask server for a wad file we need +dboolean D_NetGetWad(const char* name); + +// Netgame stuff (buffers and pointers, i.e. indices). +extern doomcom_t *doomcom; +extern doomdata_t *netbuffer; // This points inside doomcom. + +#endif diff --git a/src/d_player.h b/src/d_player.h new file mode 100644 index 0000000..9f3d221 --- /dev/null +++ b/src/d_player.h @@ -0,0 +1,254 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Player state structure. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __D_PLAYER__ +#define __D_PLAYER__ + + +// The player data structure depends on a number +// of other structs: items (internal inventory), +// animation states (closely tied to the sprites +// used to represent them, unfortunately). +#include "d_items.h" +#include "p_pspr.h" + +// In addition, the player is just a special +// case of the generic moving object/actor. +#include "p_mobj.h" + +// Finally, for odd reasons, the player input +// is buffered within the player data struct, +// as commands per game tick. +#include "d_ticcmd.h" + +#ifdef __GNUG__ +#pragma interface +#endif + + +// +// Player states. +// +typedef enum +{ + // Playing or camping. + PST_LIVE, + // Dead on the ground, view follows killer. + PST_DEAD, + // Ready to restart/respawn??? + PST_REBORN + +} playerstate_t; + + +// +// Player internal flags, for cheats and debug. +// +typedef enum +{ + // No clipping, walk through barriers. + CF_NOCLIP = 1, + // No damage, no health loss. + CF_GODMODE = 2, + // Not really a cheat, just a debug aid. + CF_NOMOMENTUM = 4, + + // [RH] Monsters don't target + CF_NOTARGET = 8, + // [RH] Flying player + CF_FLY = 16, +} 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]; + dboolean cards[NUMCARDS]; + dboolean backpack; + + // Frags, kills of other players. + int frags[MAXPLAYERS]; + weapontype_t readyweapon; + + // Is wp_nochange if not changing. + weapontype_t pendingweapon; + + dboolean 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. // CPhipps - const + 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. + dboolean didsecret; + + // e6y + // All non original (new) fields of player_t struct are moved to bottom + // for compatibility with overflow (from a deh) of player_t::ammo[NUMAMMO] + + /* killough 10/98: used for realistic bobbing (i.e. not simply overall speed) + * mo->momx and mo->momy represent true momenta experienced by player. + * This only represents the thrust that the player applies himself. + * This avoids anomolies with such things as Boom ice and conveyors. + */ + fixed_t momx, momy; // killough 10/98 + + //e6y + int resurectedkillcount; + //not used, not removed because of savagame compatibility + const char* centermessage; + + fixed_t prev_viewz; + angle_t prev_viewangle; + angle_t prev_viewpitch; + fixed_t jumpTics; // delay the next jump for a moment +} player_t; + + +// +// INTERMISSION +// Structure passed e.g. to WI_Start(wb) +// +typedef struct +{ + dboolean 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) + struct MapEntry *lastmapinfo; + + // if true, splash the secret level + dboolean didsecret; + + // previous and next levels, origin 0 + int last; + int next; + int nextep; // for when MAPINFO progression crosses into another episode. + struct MapEntry *nextmapinfo; + + 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]; + + // CPhipps - total game time for completed levels so far + int totaltimes; + +} wbstartstruct_t; + + +#endif diff --git a/src/d_server.c b/src/d_server.c new file mode 100644 index 0000000..39c2e3c --- /dev/null +++ b/src/d_server.c @@ -0,0 +1,744 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Network game server code + * New for LxDoom, but drawing ideas and code fragments from the + * earlier net code + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include + +#ifdef USE_SDL_NET + #include "SDL.h" +#endif + +#include "doomtype.h" +#include "protocol.h" +#include "i_network.h" +#ifndef PRBOOM_SERVER +#include "m_fixed.h" +#endif +#include "i_system.h" +#include "m_swap.h" + +#ifndef HAVE_GETOPT +/* The following code for getopt is from the libc-source of FreeBSD, + * it might be changed a little bit. + * Florian Schulze (florian.schulze@gmx.net) + */ + +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95"; +#endif /* LIBC_SCCS and not lint */ + +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +char *__progname="prboom_server"; + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt(nargc, nargv, ostr) + int nargc; + char * const nargv[]; + const char *ostr; +{ + extern char *__progname; + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + + if (optreset || *place == 0) { /* update scanning pointer */ + optreset = 0; + place = nargv[optind]; + if (optind >= nargc || *place++ != '-') { + /* Argument is absent or is not an option */ + place = EMSG; + return (-1); + } + optopt = *place++; + if (optopt == '-' && *place == 0) { + /* "--" => end of options */ + ++optind; + place = EMSG; + return (-1); + } + if (optopt == 0) { + /* Solitary '-', treat as a '-' option + if the program (eg su) is looking for it. */ + place = EMSG; + if (strchr(ostr, '-') == NULL) + return (-1); + optopt = '-'; + } + } else + optopt = *place++; + + /* See if option letter is one the caller wanted... */ + if (optopt == ':' || (oli = strchr(ostr, optopt)) == NULL) { + if (*place == 0) + ++optind; + if (opterr && *ostr != ':') + (void)fprintf(stderr, + "%s: illegal option -- %c\n", __progname, optopt); + return (BADCH); + } + + /* Does this option need an argument? */ + if (oli[1] != ':') { + /* don't need argument */ + optarg = NULL; + if (*place == 0) + ++optind; + } else { + /* Option-argument is either the rest of this argument or the + entire next argument. */ + if (*place) + optarg = place; + else if (nargc > ++optind) + optarg = nargv[optind]; + else { + /* option-argument absent */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (opterr) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + __progname, optopt); + return (BADCH); + } + place = EMSG; + ++optind; + } + return (optopt); /* return option letter */ +} +#else +#include +#endif + +#define MAXPLAYERS 4 +#define BACKUPTICS 12 + +// Dummies to forfill l_udp.c unused client stuff +int M_CheckParm(const char* p) { p = NULL; return 1; } +int myargc; +char** myargv; + +void NORETURN I_Error(const char *error, ...) // killough 3/20/98: add const +{ + va_list argptr; + va_start(argptr,error); + vfprintf(stderr,error,argptr); + va_end(argptr); + exit(-1); +} + +int playerjoingame[MAXPLAYERS], playerleftgame[MAXPLAYERS]; +UDP_CHANNEL remoteaddr[MAXPLAYERS]; +enum { pc_unused, pc_connected, pc_ready, pc_confirmedready, pc_playing, pc_quit } playerstate[MAXPLAYERS]; +int displaycounter; + +dboolean n_players_in_state(int n, int ps) { + int i,j; + for (i=j=0;itic); +} + + + +void read_config_file(FILE* fp, struct setup_packet_s* sp) +{ + byte* gameopt = sp->game_options; + + while (!feof(fp)) { + char def[80]; + char strparm[100]; + if (fscanf (fp, "%79s %99[^\n]\n", def, strparm) == 2) { + int v = atoi(strparm); + if (!strcmp(def,"default_skill")) { + if (verbose) printf("config file sets default_skill to %d\n",v); + sp->skill = v-1; + } else if (!strcmp(def,"default_compatibility_level")) { + if (verbose) printf("config file sets compatibility_level to %d\n",v); + if (v == -1) v = MAX_COMPATIBILITY_LEVEL-1; //e6y: -1 => maxcompat + sp->complevel = v; + } else { + int i; + for (i=0; i= MAXPLAYERS); } + +int main(int argc, char** argv) +{ +#ifndef USE_SDL_NET + int localport = 5030; +#else + Uint16 localport = 5030; +#endif + int numplayers = 2, xtratics = 0, ticdup = 1; + int exectics = 0; // gametics completed + struct setup_packet_s setupinfo = { 2, 0, 1, 1, 1, 0, best_compatibility, 0, 0}; + char**wadname = NULL; + char**wadget = NULL; + int numwads = 0; + { + int opt; + byte *gameopt = setupinfo.game_options; + + memcpy(gameopt, &def_game_options, sizeof (setupinfo.game_options)); + while ((opt = getopt(argc, argv, "c:t:x:p:e:l:adrfns:N:vw:")) != EOF) + switch (opt) { + case 'c': + { + FILE *cf = fopen(optarg,"r"); + if (!cf) { perror("fopen"); return -1; } + read_config_file(cf,&setupinfo); + fclose(cf); + } + break; + case 't': + if (optarg) ticdup = atoi(optarg); + break; + case 'x': + if (optarg) xtratics = atoi(optarg); + break; + case 'p': + if (optarg) localport = atoi(optarg); + break; + case 'e': + if (optarg) setupinfo.episode = atoi(optarg); + break; + case 'l': + if (optarg) setupinfo.level = atoi(optarg); + break; + case 'a': + setupinfo.deathmatch = 2; + break; + case 'd': + setupinfo.deathmatch = 1; + break; + case 'r': + setupinfo.game_options[6] = 1; + break; + case 'f': + setupinfo.game_options[7] = 1; + break; + case 'n': + setupinfo.game_options[8] = 1; + break; + case 's': + if (optarg) setupinfo.skill = atoi(optarg)-1; + break; + case 'N': + if (optarg) setupinfo.players = numplayers = atoi(optarg); + break; + case 'v': + verbose++; + break; + case 'w': + if (optarg) { + char *p; + wadname = realloc(wadname, ++numwads * sizeof *wadname); + wadget = realloc(wadget , numwads * sizeof *wadget ); + wadname[numwads-1] = strdup(optarg); + if ((p = strchr(wadname[numwads-1], ','))) { + *p++ = 0; wadget[numwads-1] = p; + } else wadget[numwads-1] = NULL; + } + break; + } + } + + setupinfo.ticdup = ticdup; setupinfo.extratic = xtratics; + { /* Random number seed + * Mirrors the corresponding code in G_ReadOptions */ + int rngseed = (int)time(NULL); + setupinfo.game_options[13] = rngseed & 0xff; + rngseed >>= 8; + setupinfo.game_options[12] = rngseed & 0xff; + rngseed >>= 8; + setupinfo.game_options[11] = rngseed & 0xff; + rngseed >>= 8; + setupinfo.game_options[10] = rngseed & 0xff; + } + I_InitSockets(localport); + + printf("Listening on port %d, waiting for %d players\n", localport, numplayers); + + { // no players initially + int i; + for (i=0; i2) printf("Received packet:"); + switch (packet->type) { + case PKT_INIT: + if (!ingame) { + { + int n; + struct setup_packet_s *sinfo = (void*)(packet+1); + + /* Find player number and add to the game */ + n = *(short*)(packet+1); + + if (badplayer(n) || playerstate[n] != pc_unused) + for (n=0; nyourplayer = n; + sinfo->numwads = numwads; + for (i=0; iwadnames + extrabytes, wadname[i]); + extrabytes += strlen(wadname[i]) + 1; + } + I_SendPacketTo(packet, sizeof *packet + sizeof setupinfo + extrabytes, + remoteaddr+n); + I_uSleep(10000); + I_SendPacketTo(packet, sizeof *packet + sizeof setupinfo + extrabytes, + remoteaddr+n); + } + } + } + break; + case PKT_GO: + if (!ingame) { + int from = *(byte*)(packet+1); + + if (badplayer(from) || playerstate[from] == pc_unused) break; + if (confirming) { + if (playerstate[from] != pc_confirmedready) curplayers++; + playerstate[from] = pc_confirmedready; + } else + playerstate[from] = pc_ready; + } + break; + case PKT_TICC: + { + byte tics = *(byte*)(packet+1); + int from = *(((byte*)(packet+1))+1); + + if (badplayer(from)) break; + + if (verbose>2) + printf("tics %ld - %ld from %d\n", ptic(packet), ptic(packet) + tics - 1, from); + if (ptic(packet) > remoteticfrom[from]) { + // Missed tics, so request a resend + packet_set(packet, PKT_RETRANS, remoteticfrom[from]); + I_SendPacketTo(packet, sizeof *packet, remoteaddr+from); + } else { + ticcmd_t *newtic = (void*)(((byte*)(packet+1))+2); + if (ptic(packet) + tics < remoteticfrom[from]) break; // Won't help + remoteticfrom[from] = ptic(packet); + while (tics--) + netcmds[from][remoteticfrom[from]++%BACKUPTICS] = *newtic++; + } + } + break; + case PKT_RETRANS: + { + int from = *(byte*)(packet+1); + if (badplayer(from)) break; + + if (verbose>2) printf("%d requests resend from %ld\n", from, ptic(packet)); + remoteticto[from] = ptic(packet); + } + break; + case PKT_QUIT: + { + int from = *(byte*)(packet+1); + if (badplayer(from)) break; + + if (!ingame && playerstate[from] != pc_unused) { + // If we already got a PKT_GO, we have to remove this player frmo the count of ready players. And we then flag this player slot as vacant. + printf("player %d pulls out\n", from); + if (playerstate[from] == pc_confirmedready) curplayers--; + playerstate[from] = pc_unused; + } else + if (playerleftgame[from] == INT_MAX) { // In the game + playerleftgame[from] = ptic(packet); + --curplayers; + if (verbose) printf("%d quits at %ld (%d left)\n", from, ptic(packet),curplayers); + if (ingame && !curplayers) exit(0); // All players have exited + } + } + // fallthrough + // and broadcast it + case PKT_EXTRA: + BroadcastPacket(packet, len); + if (packet->type == PKT_EXTRA) { + if (verbose>2) printf("misc from %d\n", *(((byte*)(packet+1))+1)); + } + break; + case PKT_WAD: + { + int i; + int from = *(byte*)(packet+1); + char *name = 1 + (char*)(packet+1); + size_t size = sizeof(packet_header_t); + packet_header_t *reply; + + if (badplayer(from) || playerstate[from] != pc_unused) break; + + if (verbose) printf("Request for %s ", name); + for (i=0; itype); + break; + } + } + free(packet); + if (!ingame && n_players_in_state(numplayers,pc_confirmedready)) { + int i; + packet_header_t gopacket; + packet = &gopacket; + ingame=true; + printf("All players joined, beginning game.\n"); + for (i=0; i1) printf("%d new tics can be run\n", lowtic - exectics); + + if (lowtic > exectics) + exectics = lowtic; // count exec'ed tics + // Now send all tics up to lowtic + for (i=0; i1) printf("sending %d tics to %d\n", tics, i); + while (tics--) { + int j, playersthistic = 0; + byte *q = p++; + for (j=0; j remoteticto[i])) { + *p++ = j; + memcpy(p, &netcmds[j][remoteticto[i]%BACKUPTICS], sizeof(ticcmd_t)); + p += sizeof(ticcmd_t); + playersthistic++; + } + *q = playersthistic; + remoteticto[i]++; + } + I_SendPacketTo(packet, p - ((byte*)packet), remoteaddr+i); + free(packet); + } + { + if (remoteticfrom[i] == remoteticto[i]) { + backoffcounter[i] = 0; + } else if (remoteticfrom[i] > remoteticto[i]+1) { + if ((backoffcounter[i] += remoteticfrom[i] - remoteticto[i] - 1) > 35) { + packet_header_t *packet = malloc(sizeof(packet_header_t)); + packet_set(packet, PKT_BACKOFF, remoteticto[i]); + I_SendPacketTo(packet,sizeof *packet,remoteaddr+i); + backoffcounter[i] = 0; + if (verbose) printf("telling client %d to back off\n",i); + free(packet); + } + } + } + } + } + if (!((ingame ? 0xff : 0xf) & displaycounter++)) { + int i; + fprintf(stderr,"Player states: ["); + for (i=0;i +#include +#include +#include +#include + +// this should go here, not in makefile/configure.ac -- josh +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#include "m_swap.h" +#include "version.h" +#include "doomtype.h" + +extern dboolean bfgedition; + +// Game mode handling - identify IWAD version +// to handle IWAD dependend animations etc. +typedef enum { + shareware, // DOOM 1 shareware, E1, M9 + registered, // DOOM 1 registered, E3, M27 + commercial, // DOOM 2 retail, E1 M34 (DOOM 2 german edition not handled) + retail, // DOOM 1 retail, E4, M36 + indetermined // Well, no IWAD found. +} GameMode_t; + +// Mission packs - might be useful for TC stuff? +typedef enum { + doom, // DOOM 1 + doom2, // DOOM 2 + pack_tnt, // TNT mission pack + pack_plut, // Plutonia pack + pack_nerve, // No Rest For The Living + hacx, // HACX - Twitch 'n Kill + chex, // Chex Quest + none +} GameMission_t; + +// Identify language to use, software localization. +typedef enum { + english, + french, + german, + unknown +} Language_t; + +// +// For resize of screen, at start of game. +// + +#define BASE_WIDTH 320 + +// It is educational but futile to change this +// scaling e.g. to 2. Drawing of status bar, +// menues etc. is tied to the scale implied +// by the graphics. + +#define INV_ASPECT_RATIO 0.625 /* 0.75, ideally */ + +// killough 2/8/98: MAX versions for maximum screen sizes +// allows us to avoid the overhead of dynamic allocation +// when multiple screen sizes are supported + +// SCREENWIDTH and SCREENHEIGHT define the visible size +extern int SCREENWIDTH; +extern int SCREENHEIGHT; +// SCREENPITCH is the size of one line in the buffer and +// can be bigger than the SCREENWIDTH depending on the size +// of one pixel (8, 16 or 32 bit) and the padding at the +// end of the line caused by hardware considerations +extern int SCREENPITCH; + +// e6y: wide-res +extern int WIDE_SCREENWIDTH; +extern int WIDE_SCREENHEIGHT; +extern int SCREEN_320x200; + +// The maximum number of players, multiplayer/networking. +#define MAXPLAYERS 4 + +// phares 5/14/98: +// DOOM Editor Numbers (aka doomednum in mobj_t) + +#define DEN_PLAYER5 4001 +#define DEN_PLAYER6 4002 +#define DEN_PLAYER7 4003 +#define DEN_PLAYER8 4004 + +// State updates, number of tics / second. +#define TICRATE 35 + +// The current state of the game: whether we are playing, gazing +// at the intermission screen, the game final animation, or a demo. + +typedef enum { + GS_LEVEL, + GS_INTERMISSION, + GS_FINALE, + GS_DEMOSCREEN +} gamestate_t; + +// +// Difficulty/skill settings/filters. +// +// These are Thing flags + +// 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 + +/* killough 11/98 */ +#define MTF_NOTSINGLE 16 +#define MTF_NOTDM 32 +#define MTF_NOTCOOP 64 +#define MTF_FRIEND 128 +#define MTF_RESERVED 256 + +typedef enum { + sk_none=-1, //jff 3/24/98 create unpicked skill setting + sk_baby=0, + sk_easy, + sk_medium, + sk_hard, + sk_nightmare +} skill_t; + +// +// Key cards. +// + +typedef enum { + it_bluecard, + it_yellowcard, + it_redcard, + it_blueskull, + it_yellowskull, + it_redskull, + NUMCARDS +} card_t; + +// The defined weapons, including a marker +// indicating user has not changed weapon. +typedef enum { + wp_fist, + wp_pistol, + wp_shotgun, + wp_chaingun, + wp_missile, + wp_plasma, + wp_bfg, + wp_chainsaw, + wp_supershotgun, + + NUMWEAPONS, + wp_nochange // No pending weapon change. +} 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). +typedef enum { + INVULNTICS = (30*TICRATE), + INVISTICS = (60*TICRATE), + INFRATICS = (120*TICRATE), + IRONTICS = (60*TICRATE) +} powerduration_t; + +// DOOM keyboard definition. +// This is the stuff configured by Setup.Exe. +// Most key data are simple ascii (uppercased). + +#define KEYD_RIGHTARROW 0xae +#define KEYD_LEFTARROW 0xac +#define KEYD_UPARROW 0xad +#define KEYD_DOWNARROW 0xaf +#define KEYD_ESCAPE 27 +#define KEYD_ENTER 13 +#define KEYD_TAB 9 +#define KEYD_F1 (0x80+0x3b) +#define KEYD_F2 (0x80+0x3c) +#define KEYD_F3 (0x80+0x3d) +#define KEYD_F4 (0x80+0x3e) +#define KEYD_F5 (0x80+0x3f) +#define KEYD_F6 (0x80+0x40) +#define KEYD_F7 (0x80+0x41) +#define KEYD_F8 (0x80+0x42) +#define KEYD_F9 (0x80+0x43) +#define KEYD_F10 (0x80+0x44) +#define KEYD_F11 (0x80+0x57) +#define KEYD_F12 (0x80+0x58) +#define KEYD_BACKSPACE 127 +#define KEYD_PAUSE 0xff +#define KEYD_EQUALS 0x3d +#define KEYD_MINUS 0x2d +#define KEYD_RSHIFT (0x80+0x36) +#define KEYD_RCTRL (0x80+0x1d) +#define KEYD_RALT (0x80+0x38) +#define KEYD_LALT KEYD_RALT +#define KEYD_CAPSLOCK 0xba // phares +#define KEYD_PRINTSC 0xfe + +// phares 3/2/98: +#define KEYD_INSERT 0xd2 +#define KEYD_HOME 0xc7 +#define KEYD_PAGEUP 0xc9 +#define KEYD_PAGEDOWN 0xd1 +#define KEYD_DEL 0xc8 +#define KEYD_END 0xcf +#define KEYD_SCROLLLOCK 0xc6 +#define KEYD_SPACEBAR 0x20 +// phares 3/2/98 + +#define KEYD_NUMLOCK 0xC5 // killough 3/6/98 + +// cph - Add the numeric keypad keys, as suggested by krose 4/22/99: +// The way numbers are assigned to keys is a mess, but it's too late to +// change that easily. At least these additions are don neatly. +// Codes 0x100-0x200 are reserved for number pad + +#define KEYD_KEYPAD0 (0x100 + '0') +#define KEYD_KEYPAD1 (0x100 + '1') +#define KEYD_KEYPAD2 (0x100 + '2') +#define KEYD_KEYPAD3 (0x100 + '3') +#define KEYD_KEYPAD4 (0x100 + '4') +#define KEYD_KEYPAD5 (0x100 + '5') +#define KEYD_KEYPAD6 (0x100 + '6') +#define KEYD_KEYPAD7 (0x100 + '7') +#define KEYD_KEYPAD8 (0x100 + '8') +#define KEYD_KEYPAD9 (0x100 + '9') +#define KEYD_KEYPADENTER (0x100 + KEYD_ENTER) +#define KEYD_KEYPADDIVIDE (0x100 + '/') +#define KEYD_KEYPADMULTIPLY (0x100 + '*') +#define KEYD_KEYPADMINUS (0x100 + '-') +#define KEYD_KEYPADPLUS (0x100 + '+') +#define KEYD_KEYPADPERIOD (0x100 + '.') + +// haleyjd: virtual keys +#define KEYD_MOUSE1 (0x80 + 0x60) +#define KEYD_MOUSE2 (0x80 + 0x61) +#define KEYD_MOUSE3 (0x80 + 0x62) +#define KEYD_MWHEELUP (0x80 + 0x6b) +#define KEYD_MWHEELDOWN (0x80 + 0x6c) + +// phares 4/19/98: +// Defines Setup Screen groups that config variables appear in. +// Used when resetting the defaults for every item in a Setup group. + +typedef enum { + ss_none, + ss_keys, + ss_weap, + ss_stat, + ss_auto, + ss_enem, + ss_mess, + ss_chat, + ss_gen, /* killough 10/98 */ + ss_comp, /* killough 10/98 */ + ss_max +} ss_types; + +// phares 3/20/98: +// +// Player friction is variable, based on controlling +// linedefs. More friction can create mud, sludge, +// magnetized floors, etc. Less friction can create ice. + +#define MORE_FRICTION_MOMENTUM 15000 // mud factor based on momentum +#define ORIG_FRICTION 0xE800 // original value +#define ORIG_FRICTION_FACTOR 2048 // original value +#define FRICTION_FLY 0xeb00 + +#endif // __DOOMDEF__ diff --git a/src/doomstat.c b/src/doomstat.c new file mode 100644 index 0000000..3a59937 --- /dev/null +++ b/src/doomstat.c @@ -0,0 +1,123 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Put all global state variables here. + * + *----------------------------------------------------------------------------- + */ + +#ifdef __GNUG__ +#pragma implementation "doomstat.h" +#endif +#include "doomstat.h" + +// Game Mode - identify IWAD as shareware, retail etc. +GameMode_t gamemode = indetermined; +GameMission_t gamemission = doom; + +// Language. +Language_t language = english; + +// Set if homebrew PWAD stuff has been added. +dboolean modifiedgame; + +//----------------------------------------------------------------------------- + +// CPhipps - compatibility vars +complevel_t compatibility_level, default_compatibility_level; + +// e6y +// it's required for demos recorded in "demo compatibility" mode by boom201 for example +int demover; + +int comp[COMP_TOTAL], default_comp[COMP_TOTAL]; // killough 10/98 +int /*comperr[COMPERR_NUM], */default_comperr[COMPERR_NUM]; + +// [FG] allow MBF sky transfers in all complevels +int comp_skytransfers; + +// v1.1-like pitched sounds +int pitched_sounds; // killough + +int default_translucency; // config file says // phares +dboolean general_translucency; // true if translucency is ok // phares + +int demo_insurance, default_demo_insurance; // killough 1/16/98 + +int allow_pushers = 1; // MT_PUSH Things // phares 3/10/98 +int default_allow_pushers; // killough 3/1/98: make local to each game + +int variable_friction = 1; // ice & mud // phares 3/10/98 +int default_variable_friction; // killough 3/1/98: make local to each game + +int weapon_recoil; // weapon recoil // phares +int default_weapon_recoil; // killough 3/1/98: make local to each game + +int player_bobbing; // whether player bobs or not // phares 2/25/98 +int default_player_bobbing; // killough 3/1/98: make local to each game + +int monsters_remember; // killough 3/1/98 +int default_monsters_remember; + +int monster_infighting=1; // killough 7/19/98: monster<=>monster attacks +int default_monster_infighting=1; + +int monster_friction=1; // killough 10/98: monsters affected by friction +int default_monster_friction=1; + +int dogs, default_dogs; // killough 7/19/98: Marine's best friend :) +int dog_jumping, default_dog_jumping; // killough 10/98 + +// killough 8/8/98: distance friends tend to move towards players +int distfriend = 128, default_distfriend = 128; + +// killough 9/8/98: whether monsters are allowed to strafe or retreat +int monster_backing, default_monster_backing; + +// killough 9/9/98: whether monsters are able to avoid hazards (e.g. crushers) +int monster_avoid_hazards, default_monster_avoid_hazards; + +// killough 9/9/98: whether monsters help friends +int help_friends, default_help_friends; + +int flashing_hom; // killough 10/98 + +int monkeys, default_monkeys; + +char *MAPNAME(int e, int m) +{ + static char name[9]; + + if (gamemode == commercial) + snprintf(name, sizeof(name), "MAP%02d", m); + else + snprintf(name, sizeof(name), "E%dM%d", e, m); + + return name; +} diff --git a/src/doomstat.h b/src/doomstat.h new file mode 100644 index 0000000..a1225ef --- /dev/null +++ b/src/doomstat.h @@ -0,0 +1,370 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2006 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * All the global variables that store the internal state. + * Theoretically speaking, the internal state of the engine + * should be found by looking at the variables collected + * here, and every relevant module will have to include + * this header file. + * In practice, things are a bit messy. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __D_STATE__ +#define __D_STATE__ + +// We need the playr data structure as well. +#include "d_player.h" +#include "umapinfo.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +// ------------------------ +// Command line parameters. +// + +extern dboolean nomonsters; // checkparm of -nomonsters +extern dboolean respawnparm; // checkparm of -respawn +extern dboolean fastparm; // checkparm of -fast +extern dboolean devparm; // DEBUG: launched with -devparm + +// ----------------------------------------------------- +// Game Mode - identify IWAD as shareware, retail etc. +// + +extern GameMode_t gamemode; +extern GameMission_t gamemission; +extern const char *doomverstr; + +extern char *MAPNAME(int e, int m); + +// Set if homebrew PWAD stuff has been added. +extern dboolean modifiedgame; + +// CPhipps - new compatibility handling +extern complevel_t compatibility_level, default_compatibility_level; + +// CPhipps - old compatibility testing flags aliased to new handling +#define compatibility (compatibility_level<=boom_compatibility_compatibility) +#define demo_compatibility (compatibility_level < boom_compatibility_compatibility) +#define mbf_features (compatibility_level>=mbf_compatibility) + +// v1.1-like pitched sounds +extern int pitched_sounds; // killough + +extern int default_translucency; // config file says // phares +extern dboolean general_translucency; // true if translucency is ok // phares + +extern int demo_insurance, default_demo_insurance; // killough 4/5/98 + +// ------------------------------------------- +// killough 10/98: compatibility vector + +enum { + comp_telefrag, + comp_dropoff, + comp_vile, + comp_pain, + comp_skull, + comp_blazing, + comp_doorlight, + comp_model, + comp_god, + comp_falloff, + comp_floors, + comp_skymap, + comp_pursuit, + comp_doorstuck, + comp_staylift, + comp_zombie, + comp_stairs, + comp_infcheat, + comp_zerotags, + comp_moveblock, + comp_respawn, /* cph - alias of comp_respawnfix from eternity */ + comp_sound, + comp_666, + comp_soul, + comp_maskedanim, + + //e6y + comp_ouchface, + comp_maxhealth, + comp_translucency, + + COMP_NUM, /* cph - should be last in sequence */ + COMP_TOTAL=32 // Some extra room for additional variables +}; + +enum { + comperr_zerotag, + comperr_passuse, + comperr_hangsolid, + comperr_blockmap, + comperr_allowjump, + comperr_freeaim, + + COMPERR_NUM +}; + +extern int comp[COMP_TOTAL], default_comp[COMP_TOTAL]; +extern int /*comperr[COMPERR_NUM], */default_comperr[COMPERR_NUM]; + +// [FG] allow MBF sky transfers in all complevels +extern int comp_skytransfers; + +// ------------------------------------------- +// Language. +extern Language_t language; + +// ------------------------------------------- +// Selected skill type, map etc. +// + +// Defaults for menu, methinks. +extern skill_t startskill; +extern int startepisode; +extern int startmap; + +extern dboolean autostart; + +// Selected by user. +extern skill_t gameskill; +extern int gameepisode; +extern int gamemap; +extern struct MapEntry *gamemapinfo; +extern int maplumpnum; + +// Nightmare mode flag, single player. +extern dboolean respawnmonsters; + +// Netgame? Only true if >1 player. +extern dboolean netgame; + +// Flag: true only if started as net deathmatch. +// An enum might handle altdeath/cooperative better. +extern dboolean deathmatch; + +extern dboolean coop_spawns; + +// ------------------------------------------ +// Internal parameters for sound rendering. +// These have been taken from the DOS version, +// but are not (yet) supported with Linux +// (e.g. no sound volume adjustment with menu. + +// These are not used, but should be (menu). +// From m_menu.c: +// Sound FX volume has default, 0 - 15 +// Music volume has default, 0 - 15 +// These are multiplied by 8. +extern int snd_SfxVolume; // maximum volume for sound +extern int snd_MusicVolume; // maximum volume for music + +// CPhipps - screen parameters +extern int desired_screenwidth, desired_screenheight; + +// ------------------------- +// Status flags for refresh. +// + +enum automapmode_e { + am_active = 1, // currently shown + am_overlay= 2, // covers the screen, i.e. not overlay mode + am_rotate = 4, // rotates to the player facing direction + am_follow = 8, // keep the player centred + am_grid =16, // show grid +}; +extern enum automapmode_e automapmode; // Mode that the automap is in + +enum menuactive_e { + mnact_inactive, // no menu + mnact_float, // doom-style large font menu, doesn't overlap anything + mnact_full, // boom-style small font menu, may overlap status bar +}; +extern enum menuactive_e menuactive; // Type of menu overlaid, if any + +extern dboolean paused; // Game Pause? +extern dboolean nodrawers; +extern dboolean noblit; + +// This one is related to the 3-screen display mode. +// ANG90 = left side, ANG270 = right +extern int viewangleoffset; +extern int viewpitchoffset; + +// Player taking events, and displaying. +extern int consoleplayer; +extern int displayplayer; + +// ------------------------------------- +// Scores, rating. +// Statistics on a given map, for intermission. +// +extern int totalkills, totallive; +extern int totalitems; +extern int totalsecret; +extern int show_alive; + +// Timer, for scores. +extern int basetic; /* killough 9/29/98: levelstarttic, adjusted */ +extern int leveltime; // tics in game play for par + +// -------------------------------------- +// DEMO playback/recording related stuff. + +extern dboolean usergame; +extern dboolean demoplayback; +extern dboolean demorecording; +extern int demover; + +// Quit after playing a demo from cmdline. +extern dboolean singledemo; +// Print timing information after quitting. killough +extern dboolean timingdemo; +// Run tick clock at fastest speed possible while playing demo. killough +extern dboolean fastdemo; + +extern gamestate_t gamestate; + +//----------------------------- +// Internal parameters, fixed. +// These are set by the engine, and not changed +// according to user inputs. Partly load from +// WAD, partly set at startup time. + +extern int gametic; + +//e6y +extern dboolean realframe; + +// Bookkeeping on players - state. +extern player_t players[MAXPLAYERS]; +extern int upmove; + +// Alive? Disconnected? +extern dboolean playeringame[MAXPLAYERS]; +extern dboolean realplayeringame[MAXPLAYERS]; + +extern mapthing_t *deathmatchstarts; // killough +extern size_t num_deathmatchstarts; // killough + +extern mapthing_t *deathmatch_p; + +// Player spawn spots. +extern mapthing_t playerstarts[]; + +// Intermission stats. +// Parameters for world map / intermission. +extern wbstartstruct_t wminfo; + +//----------------------------------------- +// Internal parameters, used for engine. +// + +// File handling stuff. +extern FILE *debugfile; + +// if true, load all graphics at level load +extern dboolean precache; + +// wipegamestate can be set to -1 +// to force a wipe on the next draw +extern gamestate_t wipegamestate; + +extern int mouseSensitivity_horiz; // killough +extern int mouseSensitivity_vert; + +// debug flag to cancel adaptiveness +extern dboolean singletics; + +extern int bodyqueslot; + +// Needed to store the number of the dummy sky flat. +// Used for rendering, as well as tracking projectiles etc. + +extern int skyflatnum; + +extern int maketic; + +// Networking and tick handling related. +#define BACKUPTICS 12 + +extern ticcmd_t netcmds[][BACKUPTICS]; +extern int ticdup; + +//----------------------------------------------------------------------------- + +extern int allow_pushers; // MT_PUSH Things // phares 3/10/98 +extern int default_allow_pushers; + +extern int variable_friction; // ice & mud // phares 3/10/98 +extern int default_variable_friction; + +extern int monsters_remember; // killough 3/1/98 +extern int default_monsters_remember; + +extern int weapon_recoil; // weapon recoil // phares +extern int default_weapon_recoil; + +extern int player_bobbing; // whether player bobs or not // phares 2/25/98 +extern int default_player_bobbing; // killough 3/1/98: make local to each game + +extern int dogs, default_dogs; // killough 7/19/98: Marine's best friend :) +extern int dog_jumping, default_dog_jumping; // killough 10/98 + +/* killough 8/8/98: distance friendly monsters tend to stay from player */ +extern int distfriend, default_distfriend; + +/* killough 9/8/98: whether monsters are allowed to strafe or retreat */ +extern int monster_backing, default_monster_backing; + +/* killough 9/9/98: whether monsters intelligently avoid hazards */ +extern int monster_avoid_hazards, default_monster_avoid_hazards; + +/* killough 10/98: whether monsters are affected by friction */ +extern int monster_friction, default_monster_friction; + +/* killough 9/9/98: whether monsters help friends */ +extern int help_friends, default_help_friends; + +extern int flashing_hom; // killough 10/98 + +/* killough 7/19/98: whether monsters should fight against each other */ +extern int monster_infighting, default_monster_infighting; + +extern int monkeys, default_monkeys; + +extern int HelperThing; // type of thing to use for helper + +extern dboolean forceOldBsp; + +#endif diff --git a/src/doomtype.h b/src/doomtype.h new file mode 100644 index 0000000..024ca24 --- /dev/null +++ b/src/doomtype.h @@ -0,0 +1,175 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2006 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Simple basic typedefs, isolated here to make it easier + * separating modules. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __DOOMTYPE__ +#define __DOOMTYPE__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef __BYTEBOOL__ +#define __BYTEBOOL__ +/* Fixed to use builtin bool type with C++. */ +#ifdef __cplusplus +typedef bool dboolean; +#else +typedef enum {false, true} dboolean; +#endif +typedef unsigned char byte; +#endif + +//e6y +#ifndef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif +#ifndef BETWEEN +#define BETWEEN(l,u,x) ((l)>(x)?(l):(x)>(u)?(u):(x)) +#endif + +#include + +/* cph - Wrapper for the long long type, as Win32 used a different name. + * Except I don't know what to test as it's compiler specific + * Proff - I fixed it */ +#ifndef _MSC_VER +typedef signed long long int_64_t; +typedef unsigned long long uint_64_t; +// define compiled-specific long-long contstant notation here +#define LONGLONG(num) (uint_64_t)num ## ll +#else +typedef __int64 int_64_t; +typedef unsigned __int64 uint_64_t; +// define compiled-specific long-long contstant notation here +#define LONGLONG(num) (uint_64_t)num +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +#ifndef PATH_MAX + #ifdef MAX_PATH + #define PATH_MAX MAX_PATH + #else + #define PATH_MAX 1024 + #endif +#endif + +#ifdef __GNUC__ +#define CONSTFUNC __attribute__((const)) +#define PUREFUNC __attribute__((pure)) +#define NORETURN __attribute__ ((noreturn)) +#else +#define CONSTFUNC +#define PUREFUNC +#define NORETURN +#endif + +// Definition of PACKEDATTR from Chocolate Doom +#ifdef __GNUC__ + #if defined(_WIN32) && !defined(__clang__) + #define PACKEDATTR __attribute__((packed,gcc_struct)) + #else + #define PACKEDATTR __attribute__((packed)) + #endif +#else + #define PACKEDATTR +#endif + +#ifdef WIN32 +#define C_DECL __cdecl +#else +#define C_DECL +#endif + +#ifdef _MSC_VER + #define INLINE __forceinline /* use __forceinline (VC++ specific) */ +#else + #define INLINE inline /* use standard inline */ +#endif + +/* cph - move compatibility levels here so we can use them in d_server.c */ +typedef enum { + doom_12_compatibility, /* Doom v1.2 */ + doom_1666_compatibility, /* Doom v1.666 */ + doom2_19_compatibility, /* Doom & Doom 2 v1.9 */ + ultdoom_compatibility, /* Ultimate Doom and Doom95 */ + finaldoom_compatibility, /* Final Doom */ + dosdoom_compatibility, /* DosDoom 0.47 */ + tasdoom_compatibility, /* TASDoom */ + boom_compatibility_compatibility, /* Boom's compatibility mode */ + boom_201_compatibility, /* Boom v2.01 */ + boom_202_compatibility, /* Boom v2.02 */ + lxdoom_1_compatibility, /* LxDoom v1.3.2+ */ + mbf_compatibility, /* MBF */ + prboom_1_compatibility, /* PrBoom 2.03beta? */ + prboom_2_compatibility, /* PrBoom 2.1.0-2.1.1 */ + prboom_3_compatibility, /* PrBoom 2.2.x */ + prboom_4_compatibility, /* PrBoom 2.3.x */ + prboom_5_compatibility, /* PrBoom 2.4.0 */ + prboom_6_compatibility, /* Latest PrBoom */ + MAX_COMPATIBILITY_LEVEL, /* Must be last entry */ + /* Aliases follow */ + boom_compatibility = boom_201_compatibility, /* Alias used by G_Compatibility */ + best_compatibility = prboom_6_compatibility, +} complevel_t_e; +typedef int complevel_t; + +/* cph - from v_video.h, needed by gl_struct.h */ +#define VPT_ALIGN_MASK 0xf +#define VPT_STRETCH_MASK 0x1f +enum patch_translation_e { + // e6y: wide-res + VPT_ALIGN_LEFT = 1, + VPT_ALIGN_RIGHT = 2, + VPT_ALIGN_TOP = 3, + VPT_ALIGN_LEFT_TOP = 4, + VPT_ALIGN_RIGHT_TOP = 5, + VPT_ALIGN_BOTTOM = 6, + VPT_ALIGN_WIDE = 7, + VPT_ALIGN_LEFT_BOTTOM = 8, + VPT_ALIGN_RIGHT_BOTTOM = 9, + VPT_ALIGN_MAX = 10, + VPT_STRETCH = 16, // Stretch to compensate for high-res + + VPT_NONE = 128, // Normal + VPT_FLIP = 256, // Flip image horizontally + VPT_TRANS = 512, // Translate image via a translation table + VPT_NOOFFSET = 1024, +}; + +#endif diff --git a/src/dstrings.c b/src/dstrings.c new file mode 100644 index 0000000..4e27ea5 --- /dev/null +++ b/src/dstrings.c @@ -0,0 +1,85 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Globally defined strings. + * + *----------------------------------------------------------------------------- + */ + +#ifdef __GNUG__ +#pragma implementation "dstrings.h" +#endif +#include "dstrings.h" + + +// killough 1/18/98: remove hardcoded limit, add const: +const char *const endmsg[]= +{ + // DOOM1 + QUITMSG, + "please don't leave, there's more\ndemons to toast!", + "let's beat it -- this is turning\ninto a bloodbath!", + "i wouldn't leave if i were you.\ndos is much worse.", + "you're trying to say you like dos\nbetter than me, right?", + "don't leave yet -- there's a\ndemon around that corner!", + "ya know, next time you come in here\ni'm gonna toast ya.", + "go ahead and leave. see if i care.", // 1/15/98 killough + + // QuitDOOM II messages + "you want to quit?\nthen, thou hast lost an eighth!", + "don't go now, there's a \ndimensional shambler waiting\nat the dos prompt!", + "get outta here and go back\nto your boring programs.", + "if i were your boss, i'd \n deathmatch ya in a minute!", + "look, bud. you leave now\nand you forfeit your body count!", + "just leave. when you come\nback, i'll be waiting with a bat.", + "you're lucky i don't smack\nyou for thinking about leaving.", // 1/15/98 killough + + // FinalDOOM? + +// Note that these ending "bad taste" strings were commented out +// in the original id code as the #else case of an #if 1 +// Obviously they were internal playthings before the release of +// DOOM2 and were not intended for public use. +// +// Following messages commented out for now. Bad taste. // phares + +// "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.", // 1/15/98 killough +}; + +// killough 1/18/98: remove hardcoded limit and replace with var (silly hack): +const size_t NUM_QUITMESSAGES = sizeof(endmsg)/sizeof(*endmsg) - 1; diff --git a/src/dstrings.h b/src/dstrings.h new file mode 100644 index 0000000..abb77f8 --- /dev/null +++ b/src/dstrings.h @@ -0,0 +1,80 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * DOOM strings, by language. + * Note: In BOOM, some new strings hav ebeen defined that are + * not found in the French version. A better approach is + * to create a BEX text-replacement file for other + * languages since any language can be supported that way + * without recompiling the program. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __DSTRINGS__ +#define __DSTRINGS__ + +/* All important printed strings. + * Language selection (message strings). + * Use -DFRENCH etc. + */ + +#ifdef FRENCH +#include "d_french.h" +#else +#include "d_englsh.h" +#endif + +/* Note this is not externally modifiable through DEH/BEX + * Misc. other strings. + * #define SAVEGAMENAME "boomsav" * killough 3/22/98 * + * Ty 05/04/98 - replaced with a modifiable string, see d_deh.c + */ + +/* + * File locations, + * relative to current position. + * Path names are OS-sensitive. + */ +#define DEVMAPS "devmaps" +#define DEVDATA "devdata" + + +/* Not done in french? + * QuitDOOM messages * + * killough 1/18/98: + * replace hardcoded limit with extern var (silly hack, I know) + */ + +#include + +extern const size_t NUM_QUITMESSAGES; /* Calculated in dstrings.c */ + +extern const char* const endmsg[]; /* killough 1/18/98 const added */ + +#endif diff --git a/src/e6y.c b/src/e6y.c new file mode 100644 index 0000000..bb16da4 --- /dev/null +++ b/src/e6y.c @@ -0,0 +1,1426 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#endif +#ifdef GL_DOOM +#include +#endif +#include +#include + +#include +#include +#include + +#include "SDL.h" +#ifdef _WIN32 +#include +#endif + +#include "hu_lib.h" + +#include "doomtype.h" +#include "doomstat.h" +#include "d_main.h" +#include "s_sound.h" +#include "i_system.h" +#include "i_main.h" +#include "i_sound.h" +#include "m_menu.h" +#include "lprintf.h" +#include "m_argv.h" +#include "m_misc.h" +#include "i_system.h" +#include "p_maputl.h" +#include "p_map.h" +#include "i_video.h" +#include "info.h" +#include "r_main.h" +#include "r_things.h" +#include "r_sky.h" +#include "am_map.h" +#include "hu_tracers.h" +#ifdef GL_DOOM +#include "gl_struct.h" +#include "gl_intern.h" +#endif +#include "g_game.h" +#include "r_demo.h" +#include "d_deh.h" +#include "e6y.h" + +#include "m_io.h" + +dboolean wasWiped = false; + +int secretfound; +int demo_skiptics; +int demo_playerscount; +int demo_tics_count; +int demo_curr_tic; +char demo_len_st[80]; + +int avi_shot_time; +int avi_shot_num; +const char *avi_shot_fname; + +dboolean doSkip; +dboolean demo_stoponnext; +dboolean demo_stoponend; +static dboolean demo_warp; +extern int warpepisode, warpmap; + +int key_speed_up; +int key_speed_down; +int key_speed_default; +int speed_step; +int key_level_restart; +int key_nextlevel; +int key_demo_jointogame; +int key_demo_endlevel; +int key_demo_skip; +int key_walkcamera; +int key_showalive; + +int hudadd_gamespeed; +int hudadd_leveltime; +int hudadd_demotime; +int hudadd_secretarea; +int hudadd_smarttotals; +int hudadd_demoprogressbar; +int hudadd_timests; +int hudadd_crosshair; +int hudadd_crosshair_scale; +int hudadd_crosshair_color; +int hudadd_crosshair_health; +int hudadd_crosshair_target; +int hudadd_crosshair_target_color; +int hudadd_crosshair_lock_target; +int movement_strafe50; +int movement_shorttics; +int movement_mouselook; +int movement_mousenovert; +int movement_mouseinvert; +int movement_maxviewpitch; +int movement_mousestrafedivisor; +int mouse_handler; +int mouse_doubleclick_as_use; +int mouse_carrytics; +int render_fov = 90; +int render_multisampling; +int render_paperitems; +int render_wipescreen; +int mouse_acceleration; +int demo_overwriteexisting; +int quickstart_window_ms; + +int showendoom; + +int palette_ondamage; +int palette_onbonus; +int palette_onpowers; + +float mouse_accelfactor; + +camera_t walkcamera; + +hu_textline_t w_hudadd; +hu_textline_t w_centermsg; +hu_textline_t w_precache; +char hud_add[80]; +char hud_centermsg[80]; + +int mouseSensitivity_mlook; +angle_t viewpitch; +float skyscale; +float screen_skybox_zplane; +float tan_pitch; +float skyUpAngle; +float skyUpShift; +float skyXShift; +float skyYShift; +dboolean mlook_or_fov; + +int maxViewPitch; +int minViewPitch; + +#ifdef _WIN32 +const char* WINError(void) +{ + static char *WinEBuff = NULL; + DWORD err = GetLastError(); + char *ch; + + if (WinEBuff) + { + LocalFree(WinEBuff); + } + + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &WinEBuff, 0, NULL) == 0) + { + return "Unknown error"; + } + + if ((ch = strchr(WinEBuff, '\n')) != 0) + *ch = 0; + if ((ch = strchr(WinEBuff, '\r')) != 0) + *ch = 0; + + return WinEBuff; +} +#endif + +//-------------------------------------------------- + +void e6y_assert(const char *format, ...) +{ + static FILE *f = NULL; + va_list argptr; + va_start(argptr,format); + //if (!f) + f = M_fopen("d:\\a.txt", "ab+"); + vfprintf(f, format, argptr); + fclose(f); + va_end(argptr); +} + +/* ParamsMatchingCheck + * Conflicting command-line parameters could cause the engine to be confused + * in some cases. Added checks to prevent this. + * Example: glboom.exe -record mydemo -playdemo demoname + */ +void ParamsMatchingCheck() +{ + dboolean recording_attempt = + M_CheckParm("-record") || + M_CheckParm("-recordfrom") || + M_CheckParm("-recordfromto"); + + dboolean playbacking_attempt = + M_CheckParm("-playdemo") || + M_CheckParm("-timedemo") || + M_CheckParm("-fastdemo"); + + if (recording_attempt && playbacking_attempt) + I_Error("Params are not matching: Can not being played back and recorded at the same time."); +} + +prboom_comp_t prboom_comp[PC_MAX] = { + {0xffffffff, 0x02020615, 0, "-force_monster_avoid_hazards"}, + {0x00000000, 0x02040601, 0, "-force_remove_slime_trails"}, + {0x02020200, 0x02040801, 0, "-force_no_dropoff"}, + {0x00000000, 0x02040801, 0, "-force_truncated_sector_specials"}, + {0x00000000, 0x02040802, 0, "-force_boom_brainawake"}, + {0x00000000, 0x02040802, 0, "-force_prboom_friction"}, + {0x02020500, 0x02040000, 0, "-reject_pad_with_ff"}, + {0xffffffff, 0x02040802, 0, "-force_lxdoom_demo_compatibility"}, + {0x00000000, 0x0202061b, 0, "-allow_ssg_direct"}, + {0x00000000, 0x02040601, 0, "-treat_no_clipping_things_as_not_blocking"}, + {0x00000000, 0x02040803, 0, "-force_incorrect_processing_of_respawn_frame_entry"}, + {0x00000000, 0x02040601, 0, "-force_correct_code_for_3_keys_doors_in_mbf"}, + {0x00000000, 0x02040601, 0, "-uninitialize_crush_field_for_stairs"}, + {0x00000000, 0x02040802, 0, "-force_boom_findnexthighestfloor"}, + {0x00000000, 0x02040802, 0, "-allow_sky_transfer_in_boom"}, + {0x00000000, 0x02040803, 0, "-apply_green_armor_class_to_armor_bonuses"}, + {0x00000000, 0x02040803, 0, "-apply_blue_armor_class_to_megasphere"}, + {0x02020200, 0x02050003, 0, "-force_incorrect_bobbing_in_boom"}, + {0xffffffff, 0x00000000, 0, "-boom_deh_parser"}, + {0x00000000, 0x02050007, 0, "-mbf_remove_thinker_in_killmobj"}, + {0x00000000, 0x02050007, 0, "-do_not_inherit_friendlyness_flag_on_spawn"}, + {0x00000000, 0x02050007, 0, "-do_not_use_misc12_frame_parameters_in_a_mushroom"}, + {0x00000000, 0x02050102, 0, "-apply_mbf_codepointers_to_any_complevel"}, + {0x00000000, 0x02050104, 0, "-reset_monsterspawner_params_after_loading"}, +}; + +void e6y_InitCommandLine(void) +{ + int p; + + if ((p = M_CheckParm("-skipsec")) && (p < myargc-1)) + { + float min, sec; + + if (sscanf(myargv[p+1], "%f:%f", &min, &sec) == 2) + demo_skiptics = (int) ((60 * min + sec) * TICRATE); + else if (sscanf(myargv[p+1], "%f", &sec) == 1) + demo_skiptics = (int) (sec * TICRATE); + } + + if ((IsDemoPlayback() || IsDemoContinue()) && (warpmap != -1 || demo_skiptics)) + G_SkipDemoStart(); + if ((p = M_CheckParm("-avidemo")) && (p < myargc-1)) + avi_shot_fname = myargv[p + 1]; + stats_level = M_CheckParm("-levelstat"); + + if ((stroller = M_CheckParm("-stroller"))) + { + M_AddParam("-turbo"); + M_AddParam("50"); + } + + // TAS-tracers + InitTracers(); + + shorttics = movement_shorttics || M_CheckParm("-shorttics"); +} + +static dboolean saved_fastdemo; +static dboolean saved_nodrawers; +static dboolean saved_nosfxparm; +static dboolean saved_nomusicparm; + +void G_SkipDemoStart(void) +{ + saved_fastdemo = fastdemo; + saved_nodrawers = nodrawers; + saved_nosfxparm = nosfxparm; + saved_nomusicparm = nomusicparm; + + paused = false; + + doSkip = true; + + S_StopMusic(); + fastdemo = true; + nodrawers = true; + nosfxparm = true; + nomusicparm = true; + + I_Init2(); +} + +dboolean sound_inited_once = false; + +void G_SkipDemoStop(void) +{ + fastdemo = saved_fastdemo; + nodrawers = saved_nodrawers; + nosfxparm = saved_nosfxparm; + nomusicparm = saved_nomusicparm; + + demo_stoponnext = false; + demo_stoponend = false; + demo_warp = false; + doSkip = false; + demo_skiptics = 0; + startmap = 0; + warpmap = -1; + warpepisode = -1; + + I_Init2(); + if (!sound_inited_once && !(nomusicparm && nosfxparm)) + { + I_InitSound(); + } + S_Init(snd_SfxVolume, snd_MusicVolume); + S_Stop(); + S_RestartMusic(); + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) { + gld_PreprocessLevel(); + } +#endif +} + +void G_SkipDemoStartCheck(void) +{ + if (doSkip && (gamemode == commercial ? (warpmap == gamemap) : (warpepisode == gameepisode && warpmap == gamemap))) + demo_warp = true; +} + +void G_SkipDemoCheck(void) +{ + if (doSkip && gametic > 0) + { + if (((warpmap == -1) && + (gametic > demo_skiptics + (demo_skiptics > 0 ? 0 : demo_tics_count))) || + (demo_warp && gametic - levelstarttic > demo_skiptics)) + { + G_SkipDemoStop(); + } + } +} + +int G_ReloadLevel(void) +{ + int result = false; + + if ((gamestate == GS_LEVEL) && + !deathmatch && !netgame && + !democontinue && !demoplayback && + !menuactive) + { + // restart demos from the map they were started + if (demorecording) + { + gameepisode = startepisode; + gamemap = startmap; + } + G_DeferedInitNew(gameskill, gameepisode, gamemap); + result = true; + } + + return result; +} + +// [FG] Write the episode and map number of the next level +// to the e and m pointers, respectively, or outright +// warp to this level if both are NULL. + +int G_GotoNextLevel(int *e, int *m) +{ + static byte doom2_next[33] = { + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 31, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 1, + 32, 16, 3 + }; + static byte doom_next[4][9] = { + {12, 13, 19, 15, 16, 17, 18, 21, 14}, + {22, 23, 24, 25, 29, 27, 28, 31, 26}, + {32, 33, 34, 35, 36, 39, 38, 41, 37}, + {42, 49, 44, 45, 46, 47, 48, 11, 43} + }; + int epsd; + int map = -1; + + int changed = false; + if (gamemapinfo != NULL) + { + const char *n = NULL; + if (gamemapinfo->nextsecret[0]) n = gamemapinfo->nextsecret; + else if (gamemapinfo->nextmap[0]) n = gamemapinfo->nextmap; + else if (gamemapinfo->endpic[0] && gamemapinfo->endpic[0] != '-') + { + epsd = 1; + map = 1; + } + if (n) G_ValidateMapName(n, &epsd, &map); + } + + if (map == -1) + { + // secret level + doom2_next[14] = (haswolflevels ? 31 : 16); + + if (bfgedition && singleplayer) + { + if (gamemission == pack_nerve) + { + doom2_next[3] = 9; + doom2_next[7] = 1; + doom2_next[8] = 5; + } + else + doom2_next[1] = 33; + } + + // shareware doom has only episode 1 + doom_next[0][7] = (gamemode == shareware ? 11 : 21); + + doom_next[2][7] = ((gamemode == registered) || + // the fourth episode for pre-ultimate complevels is not allowed. + (compatibility_level < ultdoom_compatibility) ? + 11 : 41); + + //doom2_next and doom_next are 0 based, unlike gameepisode and gamemap + epsd = gameepisode - 1; + map = gamemap - 1; + + if (gamemode == commercial) + { + epsd = 1; + if (map >= 0 && map <= 32) + map = doom2_next[map]; + else + map = gamemap + 1; + } + else + { + if (epsd >= 0 && epsd <= 3 && map >= 0 && map <= 8) + { + int next = doom_next[epsd][map]; + epsd = next / 10; + map = next % 10; + } + else + { + epsd = gameepisode; + map = gamemap + 1; + } + } + } + + // [FG] report next level without changing + if (e || m) + { + if (e) *e = epsd; + if (m) *m = map; + } + else if ((gamestate == GS_LEVEL) && + !deathmatch && !netgame && + !demorecording && !demoplayback && + !menuactive) + { + char *next = MAPNAME(epsd, map); + + if (W_CheckNumForName(next) == -1) + { + doom_printf("Next level not found: %s", next); + } + else + { + G_DeferedInitNew(gameskill, epsd, map); + changed = true; + } + } + + return changed; +} + +void M_ChangeSpeed(void) +{ + G_SetSpeed(); +} + +void M_ChangeMouseLook(void) +{ + viewpitch = 0; + + R_InitSkyMap(); + +#ifdef GL_DOOM + if (gl_skymode == skytype_auto) + gl_drawskys = (movement_mouselook ? skytype_skydome : skytype_standard); + else + gl_drawskys = gl_skymode; +#endif // GL_DOOM +} + +void M_ChangeMouseInvert(void) +{ +} + +void M_ChangeMaxViewPitch(void) +{ + int max_up, max_dn, angle_up, angle_dn; + + if (V_GetMode() == VID_MODEGL) + { + max_up = movement_maxviewpitch; + max_dn = movement_maxviewpitch; + } + else + { + max_up = MIN(movement_maxviewpitch, 56); + max_dn = MIN(movement_maxviewpitch, 32); + } + + angle_up = (int)((float)max_up / 45.0f * ANG45); + angle_dn = (int)((float)max_dn / 45.0f * ANG45); + + maxViewPitch = (+angle_up - (1< maxViewPitch) + *pitch = maxViewPitch; + if(*pitch < minViewPitch) + *pitch = minViewPitch; + + (*pitch) >>= 16; + (*pitch) <<= 16; +} + +int render_aspect; +float render_ratio; +float render_fovratio; +float render_fovy = FOV90; +float render_multiplier; + +void M_ChangeAspectRatio(void) +{ + extern int screenblocks; + + M_ChangeFOV(); + + R_SetViewSize(screenblocks); +} + +void M_ChangeStretch(void) +{ + extern int screenblocks; + + render_stretch_hud = render_stretch_hud_default; + + R_SetViewSize(screenblocks); +} + +void M_ChangeFOV(void) +{ + float f1, f2; + int p; + int render_aspect_width, render_aspect_height; + + if ((p = M_CheckParm("-aspect")) && (p+1 < myargc) && (strlen(myargv[p+1]) <= 21) && + (2 == sscanf(myargv[p+1], "%dx%d", &render_aspect_width, &render_aspect_height))) + { + SetRatio(SCREENWIDTH, SCREENHEIGHT); + render_fovratio = (float)render_aspect_width / (float)render_aspect_height; + render_ratio = RMUL * render_fovratio; + render_multiplier = 64.0f / render_fovratio / RMUL; + } + else + { + SetRatio(SCREENWIDTH, SCREENHEIGHT); + render_ratio = gl_ratio; + render_multiplier = (float)ratio_multiplier; + if (!tallscreen) + { + render_fovratio = 1.6f; + } + else + { + render_fovratio = render_ratio; + } + } + + render_fovy = (float)(2 * RAD2DEG(atan(tan(DEG2RAD(render_fov) / 2) / render_fovratio))); + + screen_skybox_zplane = 320.0f/2.0f/(float)tan(DEG2RAD(render_fov/2)); + + f1 = (float)(320.0f / 200.0f * (float)render_fov / (float)FOV90 - 0.2f); + f2 = (float)tan(DEG2RAD(render_fovy)/2.0f); + if (f1-f2<1) + skyUpAngle = (float)-RAD2DEG(asin(f1-f2)); + else + skyUpAngle = -90.0f; + + skyUpShift = (float)tan(DEG2RAD(render_fovy)/2.0f); + + skyscale = 1.0f / (float)tan(DEG2RAD(render_fov / 2)); +} + +#ifdef GL_DOOM +void M_ChangeMultiSample(void) +{ +} + +void M_ChangeSpriteClip(void) +{ + gl_sprite_offset = (gl_spriteclip != spriteclip_const ? 0 : (.01f * (float)gl_sprite_offset_default)); + gl_spriteclip_threshold_f = (float)gl_spriteclip_threshold / MAP_COEFF; +} + +void ResolveColormapsHiresConflict(dboolean prefer_colormap) +{ + gl_boom_colormaps = !r_have_internal_hires && !gl_texture_external_hires; +#if 0 + if (prefer_colormap) + { + if (gl_boom_colormaps_default) + { + gl_texture_external_hires = false; + } + else if (gl_texture_external_hires) + { + gl_boom_colormaps = false; + gl_boom_colormaps_default = false; + } + } + else + { + if (gl_texture_external_hires) + { + gl_boom_colormaps = false; + gl_boom_colormaps_default = false; + } + else if (gl_boom_colormaps_default) + { + gl_texture_external_hires = false; + } + } +#endif +} + +void M_ChangeAllowBoomColormaps(void) +{ + if (gl_boom_colormaps == -1) + { + gl_boom_colormaps = gl_boom_colormaps_default; + ResolveColormapsHiresConflict(true); + } + else + { + gl_boom_colormaps = gl_boom_colormaps_default; + ResolveColormapsHiresConflict(true); + gld_FlushTextures(); + gld_Precache(); + } +} + +void M_ChangeTextureUseHires(void) +{ + ResolveColormapsHiresConflict(false); + + gld_FlushTextures(); + gld_Precache(); +} + +void M_ChangeTextureHQResize(void) +{ + gld_FlushTextures(); +} +#endif //GL_DOOM + +void M_Mouse(int choice, int *sens); +void M_MouseMLook(int choice) +{ + M_Mouse(choice, &mouseSensitivity_mlook); +} + +void M_MouseAccel(int choice) +{ + M_Mouse(choice, &mouse_acceleration); + MouseAccelChanging(); +} + +void MouseAccelChanging(void) +{ + mouse_accelfactor = (float)mouse_acceleration/100.0f+1.0f; +} + +float viewPitch; +dboolean transparentpresent; + +int StepwiseSum(int value, int direction, int step, int minval, int maxval, int defval) +{ + static int prev_value = 0; + static int prev_direction = 0; + + int newvalue; + int val = (direction > 0 ? value : value - 1); + + if (direction == 0) + return defval; + + direction = (direction > 0 ? 1 : -1); + + if (step != 0) + newvalue = (prev_direction * direction < 0 ? prev_value : value + direction * step); + else + { + int exp = 1; + while (exp * 10 <= val) + exp *= 10; + newvalue = direction * (val < exp * 5 && exp > 1 ? exp / 2 : exp); + newvalue = (value + newvalue) / newvalue * newvalue; + } + + if (newvalue > maxval) newvalue = maxval; + if (newvalue < minval) newvalue = minval; + + if ((value < defval && newvalue > defval) || (value > defval && newvalue < defval)) + newvalue = defval; + + if (newvalue != value) + { + prev_value = value; + prev_direction = direction; + } + + return newvalue; +} + +void I_vWarning(const char *message, va_list argList) +{ + char msg[1024]; + doom_vsnprintf(msg,sizeof(msg),message,argList); + lprintf(LO_ERROR, "%s\n", msg); +#ifdef _WIN32 + I_MessageBox(msg, PRB_MB_OK); +#endif +} + +void I_Warning(const char *message, ...) +{ + va_list argptr; + va_start(argptr,message); + I_vWarning(message, argptr); + va_end(argptr); +} + +int I_MessageBox(const char* text, unsigned int type) +{ + int result = PRB_IDCANCEL; + +#ifdef _WIN32 + { + HWND current_hwnd = GetForegroundWindow(); + result = MessageBox(GetDesktopWindow(), text, PACKAGE_NAME, type|MB_TASKMODAL|MB_TOPMOST); + I_SwitchToWindow(current_hwnd); + return result; + } +#endif + +#if 0 + { + typedef struct mb_hotkeys_s + { + int type; + char *hotkeys_str; + } mb_hotkeys_t; + + mb_hotkeys_t mb_hotkeys[] = { + {PRB_MB_OK , "(press to continue)"}, + {PRB_MB_OKCANCEL , "(press to continue or to cancel)"}, + {PRB_MB_ABORTRETRYIGNORE , "(a - abort, r - retry, i - ignore)"}, + {PRB_MB_YESNOCANCEL , "(y - yes, n - no, esc - cancel"}, + {PRB_MB_YESNO , "(y - yes, n - no)"}, + {PRB_MB_RETRYCANCEL , "(r - retry, - cancel)"}, + {0, NULL} + }; + + int i, c; + char* hotkeys_str = NULL; + + type &= 0x000000ff; + + i = 0; + while (mb_hotkeys[i].hotkeys_str) + { + if (mb_hotkeys[i].type == type) + { + hotkeys_str = mb_hotkeys[i].hotkeys_str; + break; + } + i++; + } + + if (hotkeys_str) + { + lprintf(LO_CONFIRM, "%s\n%s\n", text, hotkeys_str); + + result = -1; + do + { + I_uSleep(1000); + + c = tolower(getchar()); + + if (c == 'y') result = PRB_IDYES; + else if (c == 'n') result = PRB_IDNO; + else if (c == 'a') result = PRB_IDABORT; + else if (c == 'r') result = PRB_IDRETRY; + else if (c == 'i') result = PRB_IDIGNORE; + else if (c == 'o' || c == 13) result = PRB_IDOK; + else if (c == 'c' || c == 27) result = PRB_IDCANCEL; + } + while (result == EOF); + + return result; + } + + return result; + } +#else + return PRB_IDCANCEL; +#endif +} + +int stats_level; +int stroller; +int numlevels = 0; +int levels_max = 0; +timetable_t *stats = NULL; + +void e6y_G_DoCompleted(void) +{ + int i; + + if (doSkip && (demo_stoponend || demo_warp)) + G_SkipDemoStop(); + + if(!stats_level) + return; + + if (numlevels >= levels_max) + { + levels_max = levels_max ? levels_max*2 : 32; + stats = realloc(stats,sizeof(*stats)*levels_max); + } + + memset(&stats[numlevels], 0, sizeof(timetable_t)); + + if (gamemode==commercial) + sprintf(stats[numlevels].map,"MAP%02i",gamemap); + else + sprintf(stats[numlevels].map,"E%iM%i",gameepisode,gamemap); + + if (secretexit) + { + size_t end_of_string = strlen(stats[numlevels].map); + if (end_of_string < 15) + stats[numlevels].map[end_of_string] = 's'; + } + + stats[numlevels].stat[TT_TIME] = leveltime; + stats[numlevels].stat[TT_TOTALTIME] = totalleveltimes; + stats[numlevels].stat[TT_TOTALKILL] = totalkills; + stats[numlevels].stat[TT_TOTALITEM] = totalitems; + stats[numlevels].stat[TT_TOTALSECRET] = totalsecret; + + for (i=0 ; i allkills_len) + allkills_len = strlen(all[level].kill); + if (strlen(all[level].item) > allitems_len) + allitems_len = strlen(all[level].item); + if (strlen(all[level].secret) > allsecrets_len) + allsecrets_len = strlen(all[level].secret); + + for(i=0; i max.stat[i]) + max.stat[i] = stats[level].stat[i]; + } + max.stat[TT_TIME] = max.stat[TT_TIME]/TICRATE/60; + max.stat[TT_TOTALTIME] = max.stat[TT_TOTALTIME]/TICRATE/60; + + for(i=0; i 0x02040802 + if ((p = M_CheckParm("-emulate")) && (p < myargc - 1)) + { + unsigned int emulated_version = 0; + int b[4], k = 1; + memset(b, 0, sizeof(b)); + sscanf(myargv[p + 1], "%d.%d.%d.%d", &b[0], &b[1], &b[2], &b[3]); + for (i = 3; i >= 0; i--, k *= 256) + { +#ifdef RANGECHECK + if (b[i] >= 256) + I_Error("Wrong version number of package: %s", PACKAGE_VERSION); +#endif + emulated_version += b[i] * k; + } + + for (i = 0; i < PC_MAX; i++) + { + prboom_comp[i].state = + (emulated_version >= prboom_comp[i].minver && + emulated_version < prboom_comp[i].maxver); + } + } + + for (i = 0; i < PC_MAX; i++) + { + if (M_CheckParm(prboom_comp[i].cmd)) + prboom_comp[i].state = true; + } + } + + P_CrossSubsector = P_CrossSubsector_PrBoom; + if (!prboom_comp[PC_FORCE_LXDOOM_DEMO_COMPATIBILITY].state) + { + if (demo_compatibility) + P_CrossSubsector = P_CrossSubsector_Doom; + + switch (compatibility_level) + { + case boom_compatibility_compatibility: + case boom_201_compatibility: + case boom_202_compatibility: + case mbf_compatibility: + P_CrossSubsector = P_CrossSubsector_Boom; + break; + } + } +} + +dboolean zerotag_manual; + +dboolean ProcessNoTagLines(line_t* line, sector_t **sec, int *secnum) +{ + zerotag_manual = false; + if (line->tag == 0 && comperr(comperr_zerotag)) + { + if (!(*sec=line->backsector)) + return true; + *secnum = (*sec)->iSectorID; + zerotag_manual = true; + return true; + } + return false; +} + +char* PathFindFileName(const char* pPath) +{ + const char* pT = pPath; + + if (pPath) + { + for ( ; *pPath; pPath++) + { + if ((pPath[0] == '\\' || pPath[0] == ':' || pPath[0] == '/') + && pPath[1] && pPath[1] != '\\' && pPath[1] != '/') + pT = pPath + 1; + } + } + + return (char*)pT; +} + +void NormalizeSlashes2(char *str) +{ + size_t l; + + if (!str || !(l = strlen(str))) + return; + if (str[--l]=='\\' || str[l]=='/') + str[l]=0; + while (l--) + if (str[l]=='/') + str[l]='\\'; +} + +unsigned int AfxGetFileName(const char* lpszPathName, char* lpszTitle, unsigned int nMax) +{ + char* lpszTemp = PathFindFileName(lpszPathName); + + if (lpszTitle == NULL) + return strlen(lpszTemp)+1; + + strncpy(lpszTitle, lpszTemp, nMax-1); + return 0; +} + +void AbbreviateName(char* lpszCanon, int cchMax, int bAtLeastName) +{ + int cchFullPath, cchFileName, cchVolName; + const char* lpszCur; + const char* lpszBase; + const char* lpszFileName; + + lpszBase = lpszCanon; + cchFullPath = strlen(lpszCanon); + + cchFileName = AfxGetFileName(lpszCanon, NULL, 0) - 1; + lpszFileName = lpszBase + (cchFullPath-cchFileName); + + if (cchMax >= cchFullPath) + return; + + if (cchMax < cchFileName) + { + strcpy(lpszCanon, (bAtLeastName) ? lpszFileName : ""); + return; + } + + lpszCur = lpszBase + 2; + + if (lpszBase[0] == '\\' && lpszBase[1] == '\\') + { + while (*lpszCur != '\\') + lpszCur++; + } + + if (cchFullPath - cchFileName > 3) + { + lpszCur++; + while (*lpszCur != '\\') + lpszCur++; + } + + cchVolName = (int)(lpszCur - lpszBase); + if (cchMax < cchVolName + 5 + cchFileName) + { + strcpy(lpszCanon, lpszFileName); + return; + } + + while (cchVolName + 4 + (int)strlen(lpszCur) > cchMax) + { + do + { + lpszCur++; + } + while (*lpszCur != '\\'); + } + + lpszCanon[cchVolName] = '\0'; + strcat(lpszCanon, "\\..."); + strcat(lpszCanon, lpszCur); +} + +int levelstarttic; + +int force_singletics_to = 0; + +int HU_DrawDemoProgress(int force) +{ + static unsigned int last_update = 0; + static int prev_len = -1; + + int len, tics_count, diff; + unsigned int tick, max_period; + + if (gamestate == GS_DEMOSCREEN || (!demoplayback && !democontinue) || !hudadd_demoprogressbar) + return false; + + tics_count = ((doSkip && demo_skiptics > 0) ? MIN(demo_skiptics, demo_tics_count) : demo_tics_count) * demo_playerscount; + len = MIN(SCREENWIDTH, (int)((int_64_t)SCREENWIDTH * demo_curr_tic / tics_count)); + + if (!force) + { + max_period = ((tics_count - demo_curr_tic > 35 * demo_playerscount) ? 500 : 15); + + // Unnecessary updates of progress bar + // can slow down demo skipping and playback + tick = SDL_GetTicks(); + if (tick - last_update < max_period) + return false; + last_update = tick; + + // Do not update progress bar if difference is small + diff = len - prev_len; + if (diff == 0 || diff == 1) // because of static prev_len + return false; + } + + prev_len = len; + + V_FillRect(0, 0, SCREENHEIGHT - 4, len - 0, 4, 4); + if (len > 4) + V_FillRect(0, 2, SCREENHEIGHT - 3, len - 4, 2, 0); + + return true; +} + +#ifdef _WIN32 +int GetFullPath(const char* FileName, const char* ext, char *Buffer, size_t BufferLength) +{ + int i, Result; + char *p; + char dir[PATH_MAX]; + + for (i=0; i<3; i++) + { + switch(i) + { + case 0: + M_getcwd(dir, sizeof(dir)); + break; + case 1: + if (!M_getenv("DOOMWADDIR")) + continue; + strcpy(dir, M_getenv("DOOMWADDIR")); + break; + case 2: + strcpy(dir, I_DoomExeDir()); + break; + } + + Result = SearchPath(dir,FileName,ext,BufferLength,Buffer,&p); + if (Result) + return Result; + } + + return false; +} +#endif + +//Begin of GZDoom code +/* +**--------------------------------------------------------------------------- +** Copyright 2004-2005 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +*/ + +//=========================================================================== +// +// smooth the edges of transparent fields in the texture +// returns false when nothing is manipulated to save the work on further +// levels + +// 28/10/2003: major optimization: This function was far too pedantic. +// taking the value of one of the neighboring pixels is fully sufficient +// +//=========================================================================== + +#ifdef WORDS_BIGENDIAN +#define MSB 0 +#define SOME_MASK 0xffffff00 +#else +#define MSB 3 +#define SOME_MASK 0x00ffffff +#endif + +#define CHKPIX(ofs) (l1[(ofs)*4+MSB]==255 ? (( ((unsigned int*)l1)[0] = ((unsigned int*)l1)[ofs]&SOME_MASK), trans=true ) : false) + +dboolean SmoothEdges(unsigned char * buffer,int w, int h) +{ + int x,y; + dboolean trans=buffer[MSB]==0; // If I set this to false here the code won't detect textures + // that only contain transparent pixels. + unsigned char * l1; + + // makes (a) no sense and (b) doesn't work with this code! + // if (h<=1 || w<=1) + // e6y: Do not smooth small patches. + // Makes sense for HUD small digits + // 2 and 7 still look ugly + if (h<=8 || w<=8) + return false; + + l1=buffer; + + if (l1[MSB]==0 && !CHKPIX(1)) CHKPIX(w); + l1+=4; + for(x=1;x + +#include "hu_lib.h" +#include "r_demo.h" + +#define HU_HUDADDX (HU_HUDX) +#define HU_HUDADDY (HU_HUDY+(-1)*HU_GAPY) +#define HU_CENTERMSGX (320/2) +#define HU_CENTERMSGY ((200-ST_HEIGHT)/2 - 1 - LittleShort(hu_font[0].height)) + +#define HU_HUDADDX_D (HU_HUDX_LL) +#define HU_HUDADDY_D (HU_HUDY_LL+(-1)*HU_GAPY) + +#define STSTR_SECRETFOUND "A secret is revealed!" + +#define S_CANT_GL_ARB_MULTITEXTURE 0x10000000 +#define S_CANT_GL_ARB_MULTISAMPLEFACTOR 0x20000000 + +#define GL_COMBINE_ARB 0x8570 +#define GL_RGB_SCALE_ARB 0x8573 + +#define FOV_CORRECTION_FACTOR (1.13776f) +#define FOV90 (90) + +typedef struct camera_s +{ + long x; + long y; + long z; + long PrevX; + long PrevY; + long PrevZ; + angle_t angle; + angle_t pitch; + angle_t PrevAngle; + angle_t PrevPitch; + int type; +} camera_t; + +extern dboolean wasWiped; + +extern int totalleveltimes; + +extern int secretfound; +extern int demo_skiptics; +extern int demo_tics_count; +extern int demo_curr_tic; +extern int demo_playerscount; +extern char demo_len_st[80]; + +extern int avi_shot_time; +extern int avi_shot_num; +extern const char *avi_shot_fname; + +extern dboolean doSkip; +extern dboolean demo_stoponnext; +extern dboolean demo_stoponend; + +extern int key_speed_up; +extern int key_speed_down; +extern int key_speed_default; +extern int speed_step; +extern int key_level_restart; +extern int key_nextlevel; +extern int key_demo_jointogame; +extern int key_demo_endlevel; +extern int key_demo_skip; +extern int key_walkcamera; +extern int key_showalive; + +extern int hudadd_gamespeed; +extern int hudadd_leveltime; +extern int hudadd_demotime; +extern int hudadd_secretarea; +extern int hudadd_smarttotals; +extern int hudadd_demoprogressbar; +extern int hudadd_timests; +extern int hudadd_crosshair; +extern int hudadd_crosshair_scale; +extern int hudadd_crosshair_color; +extern int hudadd_crosshair_health; +extern int hudadd_crosshair_target; +extern int hudadd_crosshair_target_color; +extern int hudadd_crosshair_lock_target; +extern int movement_strafe50; +extern int movement_shorttics; +extern int movement_mouselook; +extern int movement_mousenovert; +extern int movement_mouseinvert; +extern int movement_maxviewpitch; +extern int movement_mousestrafedivisor; +extern int mouse_handler; +extern int mouse_doubleclick_as_use; +extern int mouse_carrytics; +extern int render_multisampling; +extern int render_paperitems; +extern int render_wipescreen; +extern int mouse_acceleration; +extern int demo_overwriteexisting; +extern int quickstart_window_ms; + +extern int render_fov; +extern int render_aspect; +extern float render_ratio; +extern float render_fovratio; +extern float render_fovy; +extern float render_multiplier; +void M_ChangeAspectRatio(void); +void M_ChangeStretch(void); + +extern int showendoom; + +extern int palette_ondamage; +extern int palette_onbonus; +extern int palette_onpowers; + +extern camera_t walkcamera; + +extern int PitchSign; +extern int mouseSensitivity_mlook; +extern angle_t viewpitch; +extern float skyscale; +extern float screen_skybox_zplane; +extern float maxNoPitch[]; +extern float tan_pitch; +extern float skyUpAngle; +extern float skyUpShift; +extern float skyXShift; +extern float skyYShift; +extern dboolean mlook_or_fov; + +extern hu_textline_t w_hudadd; +extern hu_textline_t w_centermsg; +extern hu_textline_t w_precache; +extern char hud_add[80]; +extern char hud_centermsg[80]; + +void e6y_assert(const char *format, ...); + +void ParamsMatchingCheck(); +void e6y_InitCommandLine(void); + +void P_WalkTicker (); +void P_SyncWalkcam(dboolean sync_coords, dboolean sync_sight); +void P_ResetWalkcam(void); + +extern dboolean sound_inited_once; +void e6y_I_uSleep(unsigned long usecs); +void G_SkipDemoStart(void); +void G_SkipDemoStop(void); +void G_SkipDemoStartCheck(void); +void G_SkipDemoCheck(void); +int G_ReloadLevel(void); +int G_GotoNextLevel(int *e, int *m); + +void M_ChangeMouseLook(void); +void M_ChangeMaxViewPitch(void); +void M_ChangeMouseInvert(void); + +void M_ChangeFOV(void); + +#ifdef GL_DOOM +void M_ChangeUseDetail(void); +void M_ChangeMultiSample(void); +void M_ChangeSpriteClip(void); +void M_ChangeAllowBoomColormaps(void); +void M_ChangeTextureUseHires(void); +void M_ChangeAllowFog(void); +void M_ChangeTextureHQResize(void); +#endif +void M_ChangeSpeed(void); +void M_ChangeScreenMultipleFactor(void); +void M_ChangeInterlacedScanning(void); +void M_MouseMLook(int choice); +void M_MouseAccel(int choice); +void CheckPitch(signed int *pitch); +void I_Init2(void); + +dboolean GetMouseLook(void); +dboolean HaveMouseLook(void); + +extern float viewPitch; +extern dboolean transparentpresent; + +void R_ClearClipSegs (void); +void R_RenderBSPNode(int bspnum); + +typedef struct prboom_comp_s +{ + unsigned int minver; + unsigned int maxver; + dboolean state; + const char *cmd; +} prboom_comp_t; + +enum +{ + PC_MONSTER_AVOID_HAZARDS, + PC_REMOVE_SLIME_TRAILS, + PC_NO_DROPOFF, + PC_TRUNCATED_SECTOR_SPECIALS, + PC_BOOM_BRAINAWAKE, + PC_PRBOOM_FRICTION, + PC_REJECT_PAD_WITH_FF, + PC_FORCE_LXDOOM_DEMO_COMPATIBILITY, + PC_ALLOW_SSG_DIRECT, + PC_TREAT_NO_CLIPPING_THINGS_AS_NOT_BLOCKING, + PC_FORCE_INCORRECT_PROCESSING_OF_RESPAWN_FRAME_ENTRY, + PC_FORCE_CORRECT_CODE_FOR_3_KEYS_DOORS_IN_MBF, + PC_UNINITIALIZE_CRUSH_FIELD_FOR_STAIRS, + PC_FORCE_BOOM_FINDNEXTHIGHESTFLOOR, + PC_ALLOW_SKY_TRANSFER_IN_BOOM, + PC_APPLY_GREEN_ARMOR_CLASS_TO_ARMOR_BONUSES, + PC_APPLY_BLUE_ARMOR_CLASS_TO_MEGASPHERE, + PC_FORCE_INCORRECT_BOBBING_IN_BOOM, + PC_BOOM_DEH_PARSER, + PC_MBF_REMOVE_THINKER_IN_KILLMOBJ, + PC_DO_NOT_INHERIT_FRIENDLYNESS_FLAG_ON_SPAWN, + PC_DO_NOT_USE_MISC12_FRAME_PARAMETERS_IN_A_MUSHROOM, + PC_APPLY_MBF_CODEPOINTERS_TO_ANY_COMPLEVEL, + PC_RESET_MONSTERSPAWNER_PARAMS_AFTER_LOADING, + PC_MAX +}; + +extern prboom_comp_t prboom_comp[]; + +int StepwiseSum(int value, int direction, int step, int minval, int maxval, int defval); + +enum +{ + TT_ALLKILL, + TT_ALLITEM, + TT_ALLSECRET, + + TT_TIME, + TT_TOTALTIME, + TT_TOTALKILL, + TT_TOTALITEM, + TT_TOTALSECRET, + + TT_MAX +}; + +typedef struct timetable_s +{ + char map[16]; + + int kill[MAXPLAYERS]; + int item[MAXPLAYERS]; + int secret[MAXPLAYERS]; + + int stat[TT_MAX]; +} timetable_t; + +#ifdef _WIN32 +const char* WINError(void); +#endif + +extern int stats_level; +extern int stroller; +void e6y_G_DoCompleted(void); +void e6y_WriteStats(void); + +void e6y_G_DoWorldDone(void); + +void I_ProcessWin32Mouse(void); +void I_StartWin32Mouse(void); +void I_EndWin32Mouse(void); +int AccelerateMouse(int val); +void MouseAccelChanging(void); + +extern int mlooky; +extern int realtic_clock_rate; + +void e6y_G_Compatibility(void); + +extern dboolean zerotag_manual; + +dboolean ProcessNoTagLines(line_t* line, sector_t **sec, int *secnum); + +char* PathFindFileName(const char* pPath); +void NormalizeSlashes2(char *str); +unsigned int AfxGetFileName(const char* lpszPathName, char* lpszTitle, unsigned int nMax); +void AbbreviateName(char* lpszCanon, int cchMax, int bAtLeastName); + +//extern int viewMaxY; + +extern dboolean isskytexture; + +extern int levelstarttic; + +extern int force_singletics_to; + +int HU_DrawDemoProgress(int force); + +#ifdef _WIN32 +int GetFullPath(const char* FileName, const char* ext, char *Buffer, size_t BufferLength); +#endif + +void I_vWarning(const char *message, va_list argList); +void I_Warning(const char *message, ...); + +#define PRB_MB_OK 0x00000000 +#define PRB_MB_OKCANCEL 0x00000001 +#define PRB_MB_ABORTRETRYIGNORE 0x00000002 +#define PRB_MB_YESNOCANCEL 0x00000003 +#define PRB_MB_YESNO 0x00000004 +#define PRB_MB_RETRYCANCEL 0x00000005 +#define PRB_MB_DEFBUTTON1 0x00000000 +#define PRB_MB_DEFBUTTON2 0x00000100 +#define PRB_MB_DEFBUTTON3 0x00000200 +#define PRB_IDOK 1 +#define PRB_IDCANCEL 2 +#define PRB_IDABORT 3 +#define PRB_IDRETRY 4 +#define PRB_IDIGNORE 5 +#define PRB_IDYES 6 +#define PRB_IDNO 7 +int I_MessageBox(const char* text, unsigned int type); + +dboolean SmoothEdges(unsigned char * buffer,int w, int h); + +#endif diff --git a/src/e6y_launcher.c b/src/e6y_launcher.c new file mode 100644 index 0000000..0e4d49a --- /dev/null +++ b/src/e6y_launcher.c @@ -0,0 +1,1535 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *----------------------------------------------------------------------------- + */ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +#include "doomtype.h" +#include "w_wad.h" +#include "doomstat.h" +#include "lprintf.h" +#include "d_main.h" +#include "m_misc.h" +#include "i_system.h" +#include "m_argv.h" +#include "i_main.h" +#include ".\..\ICONS\resource.h" +#ifdef HAVE_LIBPCREPOSIX +#include "pcreposix.h" +#endif /* HAVE_LIBPCREPOSIX */ +#include "r_demo.h" +#include "e6y.h" +#include "e6y_launcher.h" + +#include "m_io.h" + +#pragma comment( lib, "comctl32.lib" ) +#pragma comment( lib, "advapi32.lib" ) + +#define ETDT_ENABLE 0x00000002 +#define ETDT_USETABTEXTURE 0x00000004 +#define ETDT_ENABLETAB (ETDT_ENABLE | ETDT_USETABTEXTURE) +typedef HRESULT (WINAPI *EnableThemeDialogTexturePROC)(HWND, DWORD); + +#define FA_DIREC 0x00000010 +#define LAUNCHER_HISTORY_SIZE 10 + +#define LAUNCHER_CAPTION PACKAGE_NAME" Launcher" + +#define I_FindName(a) ((a)->Name) +#define I_FindAttr(a) ((a)->Attribs) + +typedef struct +{ + unsigned int Attribs; + unsigned int Times[3*2]; + unsigned int Size[2]; + unsigned int Reserved[2]; + char Name[PATH_MAX]; + char AltName[14]; +} findstate_t; + +typedef struct +{ + char name[PATH_MAX]; + wad_source_t source; + dboolean doom1; + dboolean doom2; +} fileitem_t; + +typedef struct +{ + HWND HWNDServer; + HWND HWNDClient; + HWND listIWAD; + HWND listPWAD; + HWND listHistory; + HWND listCMD; + HWND staticFileName; + + fileitem_t *files; + size_t filescount; + + fileitem_t *cache; + size_t cachesize; + + int *selection; + size_t selectioncount; +} launcher_t; + +launcher_t launcher; + +launcher_enable_t launcher_enable; +const char *launcher_enable_states[launcher_enable_count] = {"never", "smart", "always"}; +char *launcher_history[LAUNCHER_HISTORY_SIZE]; + +static char launchercachefile[PATH_MAX]; + +unsigned int launcher_params; + +//global +void CheckIWAD(const char *iwadname,GameMode_t *gmode,dboolean *hassec); +void ProcessDehFile(const char *filename, const char *outfilename, int lumpnum); +const char *D_dehout(void); + +//tooltip +HWND g_hwndTT; +HHOOK g_hhk; +BOOL DoCreateDialogTooltip(void); +BOOL CALLBACK EnumChildProc(HWND hwndCtrl, LPARAM lParam); +LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam); +VOID OnWMNotify(LPARAM lParam) ; + +//common +void *I_FindFirst (const char *filespec, findstate_t *fileinfo); +int I_FindNext (void *handle, findstate_t *fileinfo); +int I_FindClose (void *handle); +char *strrtrm (char *Str); + +//events +static void L_GameOnChange(void); +static void L_FilesOnChange(void); +static void L_HistoryOnChange(void); +static void L_CommandOnChange(void); + +static void L_FillGameList(void); +static void L_FillFilesList(fileitem_t *iwad); +static void L_FillHistoryList(void); + +static char* L_HistoryGetStr(waddata_t *data); +static void L_HistoryFreeData(void); + +static void L_ReadCacheData(void); + +//selection +static void L_SelAdd(int index); +static void L_SelClearAndFree(void); +static int L_SelGetList(int **list); + +static dboolean L_GetFileType(const char *filename, fileitem_t *item); +static dboolean L_PrepareToLaunch(void); + +static dboolean L_GUISelect(waddata_t *waddata); +static dboolean L_LauncherIsNeeded(void); + +static void L_FillFilesList(fileitem_t *iwad); +static void L_AddItemToCache(fileitem_t *item); + +char* e6y_I_FindFile(const char* ext); + +//common +void *I_FindFirst (const char *filespec, findstate_t *fileinfo) +{ + return FindFirstFileA(filespec, (LPWIN32_FIND_DATAA)fileinfo); +} + +int I_FindNext (void *handle, findstate_t *fileinfo) +{ + return !FindNextFileA((HANDLE)handle, (LPWIN32_FIND_DATAA)fileinfo); +} + +int I_FindClose (void *handle) +{ + return FindClose((HANDLE)handle); +} + +#define prb_isspace(c) ((c) == 0x20) +char *strrtrm (char *str) +{ + if (str) + { + char *p = str + strlen (str)-1; + while (p >= str && prb_isspace((unsigned char) *p)) + p--; + *++p = 0; + } + return str; +} +#undef prb_isspace + + +//events +static void L_GameOnChange(void) +{ + int index; + + index = (int)SendMessage(launcher.listIWAD, CB_GETCURSEL, 0, 0); + if (index != CB_ERR) + { + index = (int)SendMessage(launcher.listIWAD, CB_GETITEMDATA, index, 0); + if (index != CB_ERR) + { + L_FillFilesList(&launcher.files[index]); + } + } +} + +static void L_FilesOnChange(void) +{ + int index; + int i, start, end; + + // , ... + start = (int)SendMessage(launcher.listPWAD, LB_GETANCHORINDEX, 0, 0); + end = (int)SendMessage(launcher.listPWAD, LB_GETCARETINDEX, 0, 0); + + for (i = start; (start=end)); (start 0) + { + index = (int)SendMessage(launcher.listPWAD, LB_GETITEMDATA, i, 0); + if (index != LB_ERR) + { + L_SelAdd(index); + } + } + } + + index = (int)SendMessage(launcher.listPWAD, LB_GETCURSEL, 0, 0); + if (index != LB_ERR) + { + index = (int)SendMessage(launcher.listPWAD, LB_GETITEMDATA, index, 0); + if (index != LB_ERR) + { + char path[PATH_MAX]; + size_t count; + RECT rect; + HFONT font, oldfont; + HDC hdc; + + strcpy(path, launcher.files[index].name); + NormalizeSlashes2(path); + M_Strlwr(path); + + hdc = GetDC(launcher.staticFileName); + GetWindowRect(launcher.staticFileName, &rect); + + font = (HFONT)SendMessage(launcher.staticFileName, WM_GETFONT, 0, 0); + oldfont = SelectObject(hdc, font); + + for (count = strlen(path); count > 0 ; count--) + { + char tmppath[PATH_MAX]; + SIZE size = {0, 0}; + strcpy(tmppath, path); + AbbreviateName(tmppath, count, false); + if (GetTextExtentPoint32(hdc, tmppath, count, &size)) + { + if (size.cx < rect.right - rect.left) + { + SendMessage(launcher.staticFileName, WM_SETTEXT, 0, (LPARAM)tmppath); + break; + } + } + } + + SelectObject(hdc, oldfont); + } + } +} + +static void L_HistoryOnChange(void) +{ + int index; + + index = (int)SendMessage(launcher.listHistory, CB_GETCURSEL, 0, 0); + if (index >= 0) + { + waddata_t *waddata; + waddata = (waddata_t*)SendMessage(launcher.listHistory, CB_GETITEMDATA, index, 0); + if ((int)waddata != CB_ERR) + { + if (!L_GUISelect(waddata)) + { + SendMessage(launcher.listHistory, CB_SETCURSEL, -1, 0); + } + } + } +} + +static DWORD L_Associate(const char *Name, const char *Ext, const char *cmdline) +{ + HKEY hKeyRoot, hKey; + DWORD result; + + hKeyRoot = HKEY_CLASSES_ROOT; + + // This creates a Root entry called 'Name' + result = RegCreateKey(hKeyRoot, Name, &hKey); + if (result != ERROR_SUCCESS) return result; + result = RegSetValue(hKey, "", REG_SZ, "PrBoom-Plus", 0); + if (result != ERROR_SUCCESS) return result; + RegCloseKey(hKey); + + // This creates a Root entry called 'Ext' associated with 'Name' + result = RegCreateKey(hKeyRoot, Ext, &hKey); + if (result != ERROR_SUCCESS) return result; + result = RegSetValue(hKey, "", REG_SZ, Name, 0); + if (result != ERROR_SUCCESS) return result; + RegCloseKey(hKey); + + // This sets the command line for 'Name' + result = RegCreateKey(hKeyRoot, Name, &hKey); + if (result != ERROR_SUCCESS) return result; + result = RegSetValue(hKey, "shell\\open\\command", REG_SZ, cmdline, strlen(cmdline) + 1); + if (result != ERROR_SUCCESS) return result; + RegCloseKey(hKey); + + return result; +} +static void L_CommandOnChange(void) +{ + int index; + + index = (int)SendMessage(launcher.listCMD, CB_GETCURSEL, 0, 0); + + switch (index) + { + case 0: + M_remove(launchercachefile); + + SendMessage(launcher.listPWAD, LB_RESETCONTENT, 0, 0); + SendMessage(launcher.listHistory, CB_SETCURSEL, -1, 0); + + if (launcher.files) + { + free(launcher.files); + launcher.files = NULL; + } + launcher.filescount = 0; + + if (launcher.cache) + { + free(launcher.cache); + launcher.cache = NULL; + } + launcher.cachesize = 0; + + e6y_I_FindFile("*.wad"); + e6y_I_FindFile("*.deh"); + e6y_I_FindFile("*.bex"); + + L_GameOnChange(); + + MessageBox(launcher.HWNDServer, "The cache has been successfully rebuilt", LAUNCHER_CAPTION, MB_OK|MB_ICONEXCLAMATION); + break; + case 1: + { + size_t i; + for (i = 0; i < sizeof(launcher_history)/sizeof(launcher_history[0]); i++) + { + char str[32]; + default_t *history; + + sprintf(str, "launcher_history%d", i); + history = M_LookupDefault(str); + + strcpy((char*)history->location.ppsz[0], ""); + } + M_SaveDefaults(); + L_FillHistoryList(); + SendMessage(launcher.listHistory, CB_SETCURSEL, -1, 0); + + MessageBox(launcher.HWNDServer, "The history has been successfully cleared", LAUNCHER_CAPTION, MB_OK|MB_ICONEXCLAMATION); + } + break; + + case 2: + case 3: + case 4: + { + DWORD result; + char *msg; + char *cmdline; + + cmdline = malloc(strlen(*myargv) + 100); + + if (cmdline) + { + sprintf(cmdline, "\"%s\" \"%%1\"", *myargv); + + result = 0; + if (index == 2) + result = L_Associate("PrBoomPlusWadFiles", ".wad", cmdline); + if (index == 3) + result = L_Associate("PrBoomPlusLmpFiles", ".lmp", cmdline); + if (index == 4) + { + strcat(cmdline, " -auto"); + result = L_Associate("PrBoomPlusLmpFiles", ".lmp", cmdline); + } + + free(cmdline); + + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, result, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *)&msg, 512, NULL)) + { + MessageBox(launcher.HWNDServer, msg, LAUNCHER_CAPTION, + MB_OK | (result == ERROR_SUCCESS ? MB_ICONASTERISK : MB_ICONEXCLAMATION)); + LocalFree(msg); + } + } + } + break; + + case 5: + { + char buf[128], next_mode[100]; + launcher_enable_t launcher_next_mode = (launcher_enable + 1) % launcher_enable_count; + + if (launcher_next_mode == launcher_enable_never) + strcpy(next_mode, "disable"); + if (launcher_next_mode == launcher_enable_smart) + strcpy(next_mode, "enable ('smart' mode)"); + if (launcher_next_mode == launcher_enable_always) + strcpy(next_mode, "enable ('always' mode)"); + + sprintf(buf, "Do you really want to %s the Launcher?", next_mode); + if (MessageBox(launcher.HWNDServer, buf, LAUNCHER_CAPTION, MB_YESNO|MB_ICONQUESTION) == IDYES) + { + launcher_enable = launcher_next_mode; + + SendMessage(launcher.listCMD, CB_DELETESTRING, index, (LPARAM)buf); + strcpy(buf, ((launcher_enable + 1) % launcher_enable_count == launcher_enable_never ? "Disable" : "Enable")); + strcat(buf, " this Launcher for future use"); + SendMessage(launcher.listCMD, CB_INSERTSTRING, index, (LPARAM)buf); + + M_SaveDefaults(); + sprintf(buf, "Successfully %s", (launcher_enable != launcher_enable_never ? "enabled" : "disabled")); + MessageBox(launcher.HWNDServer, buf, LAUNCHER_CAPTION, MB_OK|MB_ICONEXCLAMATION); + } + } + break; + } + + SendMessage(launcher.listCMD, CB_SETCURSEL, -1, 0); +} + +static dboolean IsIWADName(const char *name); +static dboolean L_GetFileType(const char *filename, fileitem_t *item) +{ + size_t i, len; + wadinfo_t header; + FILE *f; + + item->source = source_err; + item->doom1 = false; + item->doom2 = false; + strcpy(item->name, filename); + + len = strlen(filename); + + if (!strcasecmp(&filename[len-4],".deh") || !strcasecmp(&filename[len-4],".bex")) + { + item->source = source_deh; + return true; + } + + for (i = 0; i < launcher.cachesize; i++) + { + if (!strcasecmp(filename, launcher.cache[i].name)) + { + strcpy(item->name, launcher.cache[i].name); + item->source = launcher.cache[i].source; + item->doom1 = launcher.cache[i].doom1; + item->doom2 = launcher.cache[i].doom2; + return true; + } + } + + if ( (f = M_fopen (filename, "rb")) ) + { + fread (&header, sizeof(header), 1, f); + if (!strncmp(header.identification, "IWAD", 4) || + (!strncmp(header.identification, "PWAD", 4) && IsIWADName(filename))) + { + item->source = source_iwad; + } + else if (!strncmp(header.identification, "PWAD", 4)) + { + item->source = source_pwad; + } + if (item->source != source_err) + { + header.numlumps = LittleLong(header.numlumps); + if (0 == fseek(f, LittleLong(header.infotableofs), SEEK_SET)) + { + for (i = 0; !item->doom1 && !item->doom2 && i < (size_t)header.numlumps; i++) + { + filelump_t lump; + + if (0 == fread (&lump, sizeof(lump), 1, f)) + break; + + if (strlen(lump.name) == 4) + { + if ((lump.name[0] == 'E' && lump.name[2] == 'M') && + (lump.name[1] >= '1' && lump.name[1] <= '4') && + (lump.name[3] >= '1' && lump.name[3] <= '9')) + item->doom1 = true; + } + + if (strlen(lump.name) == 5) + { + if (!strncmp(lump.name, "MAP", 3) && + (lump.name[3] >= '0' && lump.name[3] <= '9') && + (lump.name[4] >= '0' && lump.name[4] <= '9')) + item->doom2 = true; + } + + } + L_AddItemToCache(item); + } + } + fclose(f); + return true; + } + return false; +} + +static dboolean L_GUISelect(waddata_t *waddata) +{ + int i, j; + size_t k; + int topindex; + dboolean processed = false; + int listIWADCount, listPWADCount; + char fullpath[PATH_MAX]; + + if (!waddata->wadfiles) + return false; + + listIWADCount = (int)SendMessage(launcher.listIWAD, CB_GETCOUNT, 0, 0); + SendMessage(launcher.listIWAD, CB_SETCURSEL, -1, 0); + + for (k=0; !processed && k < waddata->numwadfiles; k++) + { + if (GetFullPath(waddata->wadfiles[k].name, NULL, fullpath, PATH_MAX)) + { + switch (waddata->wadfiles[k].src) + { + case source_iwad: + for (i=0; !processed && (size_t)inumwadfiles; k++) + { + if (GetFullPath(waddata->wadfiles[k].name, NULL, fullpath, PATH_MAX)) + { + switch (waddata->wadfiles[k].src) + { + case source_deh: + case source_pwad: + processed = false; + for (j=0; !processed && j < listPWADCount; j++) + { + int index = (int)SendMessage(launcher.listPWAD, LB_GETITEMDATA, j, 0); + if (index != LB_ERR) + { + if (!strcasecmp(launcher.files[index].name, fullpath)) + if (SendMessage(launcher.listPWAD, LB_SETSEL, true, j) != CB_ERR) + { + if (topindex == -1) + topindex = j; + L_SelAdd(index); + processed = true; + } + } + } + if (!processed) + return false; + break; + } + } + //else + // return false; + } + + if (topindex == -1) + topindex = 0; + SendMessage(launcher.listPWAD, LB_SETTOPINDEX, topindex, 0); + + return true; +} + +static dboolean L_PrepareToLaunch(void) +{ + int i, index, listPWADCount; + char *history = NULL; + wadfile_info_t *new_wadfiles=NULL; + size_t new_numwadfiles = 0; + int *selection = NULL; + int selectioncount = 0; + + new_numwadfiles = numwadfiles; + new_wadfiles = malloc(sizeof(*wadfiles) * numwadfiles); + memcpy(new_wadfiles, wadfiles, sizeof(*wadfiles) * numwadfiles); + numwadfiles = 0; + free(wadfiles); + wadfiles = NULL; + + listPWADCount = (int)SendMessage(launcher.listPWAD, LB_GETCOUNT, 0, 0); + + index = (int)SendMessage(launcher.listIWAD, CB_GETCURSEL, 0, 0); + if (index != CB_ERR) + { + index = (int)SendMessage(launcher.listIWAD, CB_GETITEMDATA, index, 0); + if (index != CB_ERR) + { + extern void D_AutoloadIWadDir(); + char *iwadname = PathFindFileName(launcher.files[index].name); + history = malloc(strlen(iwadname) + 8); + strcpy(history, iwadname); + AddIWAD(launcher.files[index].name); + D_AutoloadIWadDir(); + } + } + + if (numwadfiles == 0) + return false; + + for (i = 0; (size_t)i < new_numwadfiles; i++) + { + if (new_wadfiles[i].src == source_auto_load || new_wadfiles[i].src == source_pre) + { + wadfiles = realloc(wadfiles, sizeof(*wadfiles)*(numwadfiles+1)); + wadfiles[numwadfiles].name = strdup(new_wadfiles[i].name); + wadfiles[numwadfiles].src = new_wadfiles[i].src; + wadfiles[numwadfiles].handle = new_wadfiles[i].handle; + numwadfiles++; + } + } + + selectioncount = L_SelGetList(&selection); + + for (i=0; i < selectioncount; i++) + { + int index = selection[i]; + fileitem_t *item = &launcher.files[index]; + + if (item->source == source_pwad || item->source == source_iwad) + { + D_AddFile(item->name, source_pwad); + modifiedgame = true; + } + + if (item->source == source_deh) + ProcessDehFile(item->name, D_dehout(),0); + + history = realloc(history, strlen(history) + strlen(item->name) + 8); + strcat(history, "|"); + strcat(history, item->name); + } + + free(selection); + L_SelClearAndFree(); + + for (i = 0; (size_t)i < new_numwadfiles; i++) + { + if (new_wadfiles[i].src == source_lmp || new_wadfiles[i].src == source_net) + D_AddFile(new_wadfiles[i].name, new_wadfiles[i].src); + if (new_wadfiles[i].name) + free((char*)new_wadfiles[i].name); + } + free(new_wadfiles); + + if (history) + { + size_t i; + char str[32]; + default_t *history1, *history2; + size_t historycount = sizeof(launcher_history)/sizeof(launcher_history[0]); + size_t shiftfrom = historycount - 1; + + for (i = 0; i < historycount; i++) + { + sprintf(str, "launcher_history%d", i); + history1 = M_LookupDefault(str); + + if (!strcasecmp(history1->location.ppsz[0], history)) + { + shiftfrom = i; + break; + } + } + + for (i = shiftfrom; i > 0; i--) + { + sprintf(str, "launcher_history%d", i); + history1 = M_LookupDefault(str); + sprintf(str, "launcher_history%d", i-1); + history2 = M_LookupDefault(str); + + if (i == shiftfrom) + free((char*)history1->location.ppsz[0]); + history1->location.ppsz[0] = history2->location.ppsz[0]; + } + if (shiftfrom > 0) + { + history1 = M_LookupDefault("launcher_history0"); + history1->location.ppsz[0] = history; + } + } + return true; +} + +static void L_AddItemToCache(fileitem_t *item) +{ + FILE *fcache; + + if ( (fcache = M_fopen(launchercachefile, "at")) ) + { + fprintf(fcache, "%s = %d, %d, %d\n",item->name, item->source, item->doom1, item->doom2); + fclose(fcache); + } +} + +static void L_ReadCacheData(void) +{ + FILE *fcache; + + if ( (fcache = M_fopen(launchercachefile, "rt")) ) + { + fileitem_t item; + char name[PATH_MAX]; + + while (fgets(name, sizeof(name), fcache)) + { + char *p = strrchr(name, '='); + if (p) + { + *p = 0; + if (3 == sscanf(p + 1, "%d, %d, %d", &item.source, &item.doom1, &item.doom2)) + { + launcher.cache = realloc(launcher.cache, sizeof(*launcher.cache) * (launcher.cachesize + 1)); + strcpy(launcher.cache[launcher.cachesize].name, M_Strlwr(strrtrm(name))); + launcher.cache[launcher.cachesize].source = item.source; + launcher.cache[launcher.cachesize].doom1 = item.doom1; + launcher.cache[launcher.cachesize].doom2 = item.doom2; + + launcher.cachesize++; + } + } + } + fclose(fcache); + } +} + +static void L_SelAdd(int index) +{ + launcher.selection = realloc(launcher.selection, + sizeof(launcher.selection[0]) * (launcher.selectioncount + 1)); + launcher.selection[launcher.selectioncount] = index; + launcher.selectioncount++; +} + +static void L_SelClearAndFree(void) +{ + free(launcher.selection); + launcher.selection = NULL; + launcher.selectioncount = 0; +} + +static int L_SelGetList(int **list) +{ + int i, j, count = 0; + int listPWADCount = (int)SendMessage(launcher.listPWAD, LB_GETCOUNT, 0, 0); + + *list = NULL; + + for (i = launcher.selectioncount - 1; i >= 0; i--) + { + dboolean present = false; + for (j = 0; j < count && !present; j++) + { + present = (*list)[j] == launcher.selection[i]; + } + + if (!present) + { + for (j=0; j < listPWADCount; j++) + { + int index = launcher.selection[i]; + if (SendMessage(launcher.listPWAD, LB_GETITEMDATA, j, 0) == index) + { + if (SendMessage(launcher.listPWAD, LB_GETSEL, j, 0) > 0) + { + *list = realloc(*list, sizeof(int) * (count + 1)); + (*list)[count++] = launcher.selection[i]; + } + } + } + } + } + + for (i = 0; i < count / 2; i++) + { + int tmp = (*list)[i]; + (*list)[i] = (*list)[count - 1 - i]; + (*list)[count - 1 - i] = tmp; + } + + return count; +} + +extern const int nstandard_iwads; +extern const char *const standard_iwads[]; + +static dboolean IsIWADName(const char *name) +{ + int i; + char *filename = PathFindFileName(name); + + for (i = 0; i < nstandard_iwads; i++) + { + if (!strcasecmp(filename, standard_iwads[i])) + { + return true; + } + } + + return false; +} + +static void L_FillGameList(void) +{ + int i, j; + + // "doom2f.wad", "doom2.wad", "plutonia.wad", "tnt.wad", + // "doom.wad", "doom1.wad", "doomu.wad", + // "freedoom2.wad", "freedoom1.wad", "freedm.wad" + // "hacx.wad", "chex.wad" + // "bfgdoom2.wad", "bfgdoom.wad" + const char *IWADTypeNames[] = + { + "DOOM 2: French Version", + "DOOM 2: Hell on Earth", + "DOOM 2: Plutonia Experiment", + "DOOM 2: TNT - Evilution", + + "DOOM Registered", + "DOOM Shareware", + "The Ultimate DOOM", + + "Freedoom: Phase 2", + "Freedoom: Phase 1", + "FreeDM", + + "HACX - Twitch 'n Kill", + "Chex(R) Quest", + "REKKR", + + "DOOM 2: BFG Edition", + "DOOM 1: BFG Edition", + }; + + for (i = 0; (size_t)i < launcher.filescount; i++) + { + fileitem_t *item = &launcher.files[i]; + if (item->source == source_iwad) + { + for (j=0; j < nstandard_iwads; j++) + { + if (!strcasecmp(PathFindFileName(item->name), standard_iwads[j])) + { + char iwadname[128]; + int index; + sprintf(iwadname, "%s (%s)", IWADTypeNames[j], standard_iwads[j]); + index = (int)SendMessage(launcher.listIWAD, CB_ADDSTRING, 0, (LPARAM)iwadname); + if (index >= 0) + SendMessage(launcher.listIWAD, CB_SETITEMDATA, index, (LPARAM)i); + } + } + } + } +} + +static void L_FillFilesList(fileitem_t *iwad) +{ + int index; + size_t i; + fileitem_t *item; + + SendMessage(launcher.listPWAD, LB_RESETCONTENT, 0, 0); + + for (i = 0; i < launcher.filescount; i++) + { + item = &launcher.files[i]; + if (iwad->doom1 && item->doom1 || iwad->doom2 && item->doom2 || + (!item->doom1 && !item->doom2) || + item->source == source_deh) + { + index = (int)SendMessage(launcher.listPWAD, LB_ADDSTRING, 0, (LPARAM)M_Strlwr(PathFindFileName(item->name))); + if (index >= 0) + { + SendMessage(launcher.listPWAD, LB_SETITEMDATA, index, i); + } + } + + } +} + +char* e6y_I_FindFile(const char* ext) +{ + int i; + /* Precalculate a length we will need in the loop */ + size_t pl = strlen(ext) + 4; + + for (i=0; i<3; i++) { + char * p; + char d[PATH_MAX]; + const char * s = NULL; + strcpy(d, ""); + switch(i) { + case 0: + M_getcwd(d, sizeof(d)); + break; + case 1: + if (!M_getenv("DOOMWADDIR")) + continue; + strcpy(d, M_getenv("DOOMWADDIR")); + break; + case 2: + strcpy(d, I_DoomExeDir()); + break; + } + + p = malloc(strlen(d) + (s ? strlen(s) : 0) + pl); + sprintf(p, "%s%s%s%s", d, (d && !HasTrailingSlash(d)) ? "\\" : "", + s ? s : "", (s && !HasTrailingSlash(s)) ? "\\" : ""); + + { + void *handle; + findstate_t findstate; + char fullmask[PATH_MAX]; + + sprintf(fullmask, "%s%s", (p?p:""), ext); + + if ((handle = I_FindFirst(fullmask, &findstate)) != (void *)-1) + { + do + { + if (!(I_FindAttr (&findstate) & FA_DIREC)) + { + fileitem_t item; + char fullpath[PATH_MAX]; + + sprintf(fullpath, "%s%s", (p?p:""), I_FindName(&findstate)); + + if (L_GetFileType(fullpath, &item)) + { + if (item.source != source_err) + { + size_t j; + dboolean present = false; + for (j = 0; !present && j < launcher.filescount; j++) + present = !strcasecmp(launcher.files[j].name, fullpath); + + if (!present) + { + launcher.files = realloc(launcher.files, sizeof(*launcher.files) * (launcher.filescount + 1)); + + strcpy(launcher.files[launcher.filescount].name, fullpath); + launcher.files[launcher.filescount].source = item.source; + launcher.files[launcher.filescount].doom1 = item.doom1; + launcher.files[launcher.filescount].doom2 = item.doom2; + launcher.filescount++; + } + } + } + } + } + while (I_FindNext (handle, &findstate) == 0); + I_FindClose (handle); + } + } + + free(p); + } + return NULL; +} + +static char* L_HistoryGetStr(waddata_t *data) +{ + size_t i; + char *iwad = NULL; + char *pwad = NULL; + char *deh = NULL; + char **str; + char *result; + size_t len; + + for (i = 0; i < data->numwadfiles; i++) + { + str = NULL; + switch (data->wadfiles[i].src) + { + case source_iwad: str = &iwad; break; + case source_pwad: str = &pwad; break; + case source_deh: str = &deh; break; + } + if (*str) + { + *str = realloc(*str, strlen(*str) + strlen(data->wadfiles[i].name) + 8); + strcat(*str, " + "); + strcat(*str, PathFindFileName(data->wadfiles[i].name)); + } + else + { + *str = malloc(strlen(data->wadfiles[i].name) + 8); + strcpy(*str, PathFindFileName(data->wadfiles[i].name)); + } + } + + len = 0; + if (iwad) len += strlen(iwad); + if (pwad) len += strlen(pwad); + if (deh) len += strlen(deh); + + result = malloc(len + 16); + strcpy(result, ""); + + if (pwad) + { + strcat(result, M_Strlwr(pwad)); + if (deh) + strcat(result, " + "); + free(pwad); + } + if (deh) + { + strcat(result, M_Strlwr(deh)); + free(deh); + } + if (iwad) + { + strcat(result, " @ "); + strcat(result, M_Strupr(iwad)); + free(iwad); + } + + return result; +} + +static void L_HistoryFreeData(void) +{ + int i, count; + count = (int)SendMessage(launcher.listHistory, CB_GETCOUNT, 0, 0); + if (count != CB_ERR) + { + for (i = 0; i < count; i++) + { + waddata_t *waddata = (waddata_t*)SendMessage(launcher.listHistory, CB_GETITEMDATA, i, 0); + if ((int)waddata != CB_ERR) + { + WadDataFree(waddata); + } + } + } +} + +static void L_FillHistoryList(void) +{ + int i; + char *p = NULL; + + L_HistoryFreeData(); + + SendMessage(launcher.listHistory, CB_RESETCONTENT, 0, 0); + + for (i = 0; i < sizeof(launcher_history)/sizeof(launcher_history[0]); i++) + { + if (strlen(launcher_history[i]) > 0) + { + int index; + char *str = strdup(launcher_history[i]); + waddata_t *waddata = malloc(sizeof(*waddata)); + memset(waddata, 0, sizeof(*waddata)); + + ParseDemoPattern(str, waddata, NULL, false); + p = L_HistoryGetStr(waddata); + + if (p) + { + index = (int)SendMessage(launcher.listHistory, CB_ADDSTRING, 0, (LPARAM)p); + if (index >= 0) + SendMessage(launcher.listHistory, CB_SETITEMDATA, index, (LPARAM)waddata); + + free(p); + p = NULL; + } + + free(str); + } + } +} + +BOOL CALLBACK LauncherClientCallback (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + { + int i; + HMODULE hMod; + waddata_t data; + + launcher.HWNDClient = hDlg; + launcher.listIWAD = GetDlgItem(launcher.HWNDClient, IDC_IWADCOMBO); + launcher.listPWAD = GetDlgItem(launcher.HWNDClient, IDC_PWADLIST); + launcher.listHistory = GetDlgItem(launcher.HWNDClient, IDC_HISTORYCOMBO); + launcher.listCMD = GetDlgItem(launcher.HWNDClient, IDC_COMMANDCOMBO); + launcher.staticFileName = GetDlgItem(launcher.HWNDClient, IDC_FULLFILENAMESTATIC); + + hMod = LoadLibrary("uxtheme.dll"); + if (hMod) + { + EnableThemeDialogTexturePROC pEnableThemeDialogTexture; + pEnableThemeDialogTexture = (EnableThemeDialogTexturePROC)GetProcAddress(hMod, "EnableThemeDialogTexture"); + if (pEnableThemeDialogTexture) + pEnableThemeDialogTexture(hDlg, ETDT_ENABLETAB); + FreeLibrary(hMod); + } + + SendMessage(launcher.listCMD, CB_ADDSTRING, 0, (LPARAM)"Rebuild the "PACKAGE_NAME" cache"); + SendMessage(launcher.listCMD, CB_ADDSTRING, 0, (LPARAM)"Clear all Launcher's history"); + SendMessage(launcher.listCMD, CB_ADDSTRING, 0, (LPARAM)"Associate the current EXE with DOOM wads"); + SendMessage(launcher.listCMD, CB_ADDSTRING, 0, (LPARAM)"... with DOOM demos"); + SendMessage(launcher.listCMD, CB_ADDSTRING, 0, (LPARAM)"... with DOOM demos (-auto mode)"); + + { + char buf[128]; + strcpy(buf, ((launcher_enable + 1) % launcher_enable_count == launcher_enable_never ? "Disable" : "Enable")); + strcat(buf, " this Launcher for future use"); + SendMessage(launcher.listCMD, CB_ADDSTRING, 0, (LPARAM)buf); + } + + DoCreateDialogTooltip(); + + SendMessage(launcher.listCMD, CB_SETCURSEL, -1, 0); + L_CommandOnChange(); + + L_ReadCacheData(); + + e6y_I_FindFile("*.wad"); + e6y_I_FindFile("*.deh"); + e6y_I_FindFile("*.bex"); + + L_FillGameList(); + L_FillHistoryList(); + + i = -1; + if (launcher_params) + { + WadDataInit(&data); + WadFilesToWadData(&data); + L_GUISelect(&data); + } + else + { +#ifdef HAVE_LIBPCREPOSIX + for (i = 0; (size_t)i < numwadfiles; i++) + { + if (wadfiles[i].src == source_lmp) + { + patterndata_t patterndata; + memset(&patterndata, 0, sizeof(patterndata)); + + if (DemoNameToWadData(wadfiles[i].name, &data, &patterndata)) + { + L_GUISelect(&data); + SendMessage(launcher.staticFileName, WM_SETTEXT, 0, (LPARAM)patterndata.pattern_name); + WadDataFree(&data); + break; + } + free(patterndata.missed); + } + } +#endif + } + + if ((size_t)i == numwadfiles) + { + if (SendMessage(launcher.listHistory, CB_SETCURSEL, 0, 0) != CB_ERR) + { + L_HistoryOnChange(); + SetFocus(launcher.listHistory); + } + else if (SendMessage(launcher.listIWAD, CB_SETCURSEL, 0, 0) != CB_ERR) + { + L_GameOnChange(); + SetFocus(launcher.listPWAD); + } + } + } + break; + + case WM_NOTIFY: + OnWMNotify(lParam); + break; + + case WM_COMMAND: + { + int wmId = LOWORD(wParam); + int wmEvent = HIWORD(wParam); + + if (wmId == IDC_PWADLIST && wmEvent == LBN_DBLCLK) + { + if (L_PrepareToLaunch()) + EndDialog (launcher.HWNDServer, 1); + } + + if (wmId == IDC_HISTORYCOMBO && wmEvent == CBN_SELCHANGE) + L_HistoryOnChange(); + + if (wmId == IDC_IWADCOMBO && wmEvent == CBN_SELCHANGE) + L_GameOnChange(); + + if (wmId == IDC_PWADLIST && wmEvent == LBN_SELCHANGE) + L_FilesOnChange(); + + if ((wmId == IDC_IWADCOMBO && wmEvent == CBN_SELCHANGE) || + (wmId == IDC_PWADLIST && wmEvent == LBN_SELCHANGE)) + SendMessage(launcher.listHistory, CB_SETCURSEL, -1, 0); + + if (wmId == IDC_COMMANDCOMBO && wmEvent == CBN_SELCHANGE) + L_CommandOnChange(); + } + break; + } + return FALSE; + } + +BOOL CALLBACK LauncherServerCallback (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + + switch (message) + { + case WM_COMMAND: + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + switch (wmId) + { + case IDCANCEL: + EndDialog (hWnd, 0); + break; + case IDOK: + if (L_PrepareToLaunch()) + { + EndDialog (hWnd, 1); + } + break; + } + break; + + case WM_INITDIALOG: + launcher.HWNDServer = hWnd; + CreateDialogParam(GetModuleHandle(NULL), + MAKEINTRESOURCE(IDD_LAUNCHERCLIENTDIALOG), + launcher.HWNDServer, + LauncherClientCallback, 0); + break; + + case WM_DESTROY: + L_HistoryFreeData(); + break; + } + return 0; +} + +// DoCreateDialogTooltip - creates a tooltip control for a dialog box, +// enumerates the child control windows, and installs a hook +// procedure to monitor the message stream for mouse messages posted +// to the control windows. +// Returns TRUE if successful or FALSE otherwise. +// +// Global variables +// g_hwndTT - handle of the tooltip control +// g_hhk - handle of the hook procedure + +BOOL DoCreateDialogTooltip(void) +{ + // Ensure that the common control DLL is loaded, and create a tooltip control. + g_hwndTT = CreateWindowEx(0, TOOLTIPS_CLASS, (LPSTR) NULL, + TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, launcher.HWNDClient, (HMENU) NULL, GetModuleHandle(NULL), NULL); + + if (g_hwndTT == NULL) + return FALSE; + + // Enumerate the child windows to register them with the tooltip control. + if (!EnumChildWindows(launcher.HWNDClient, (WNDENUMPROC) EnumChildProc, 0)) + return FALSE; + + // Install a hook procedure to monitor the message stream for mouse + // messages intended for the controls in the dialog box. + g_hhk = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, + (HINSTANCE)NULL, GetCurrentThreadId()); + + if (g_hhk == (HHOOK) NULL) + return FALSE; + + return TRUE; +} + +// EmumChildProc - registers control windows with a tooltip control by +// using the TTM_ADDTOOL message to pass the address of a TOOLINFO structure. +// Returns TRUE if successful or FALSE otherwise. +// hwndCtrl - handle of a control window +// lParam - application-defined value (not used) +BOOL CALLBACK EnumChildProc(HWND hwndCtrl, LPARAM lParam) +{ + TOOLINFO ti; + char szClass[64]; + + // Skip static controls. + GetClassName(hwndCtrl, szClass, sizeof(szClass)); + if (strcmp(szClass, "STATIC")) + { + ti.cbSize = sizeof(TOOLINFO); + ti.uFlags = TTF_IDISHWND; + + ti.hwnd = launcher.HWNDClient; + ti.uId = (UINT) hwndCtrl; + ti.hinst = 0; + ti.lpszText = LPSTR_TEXTCALLBACK; + SendMessage(g_hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti); + } + return TRUE; +} + +// GetMsgProc - monitors the message stream for mouse messages intended +// for a control window in the dialog box. +// Returns a message-dependent value. +// nCode - hook code +// wParam - message flag (not used) +// lParam - address of an MSG structure +LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + MSG *lpmsg; + + lpmsg = (MSG *) lParam; + if (nCode < 0 || !(IsChild(launcher.HWNDClient, lpmsg->hwnd))) + return (CallNextHookEx(g_hhk, nCode, wParam, lParam)); + + switch (lpmsg->message) + { + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + if (g_hwndTT != NULL) + { + MSG msg; + + msg.lParam = lpmsg->lParam; + msg.wParam = lpmsg->wParam; + msg.message = lpmsg->message; + msg.hwnd = lpmsg->hwnd; + SendMessage(g_hwndTT, TTM_RELAYEVENT, 0, (LPARAM) (LPMSG) &msg); + } + break; + default: + break; + } + return (CallNextHookEx(g_hhk, nCode, wParam, lParam)); +} + +// OnWMNotify - provides the tooltip control with the appropriate text +// to display for a control window. This function is called by +// the dialog box procedure in response to a WM_NOTIFY message. +// lParam - second message parameter of the WM_NOTIFY message +VOID OnWMNotify(LPARAM lParam) +{ + static char *tooltip_str = NULL; + static int tooltip_maxlen = 0; + + LPTOOLTIPTEXT lpttt; + int idCtrl; + + if ((((LPNMHDR) lParam)->code) == TTN_NEEDTEXT) + { + idCtrl = GetDlgCtrlID((HWND) ((LPNMHDR) lParam)->idFrom); + lpttt = (LPTOOLTIPTEXT) lParam; + + switch (idCtrl) + { + case IDC_HISTORYCOMBO: + case IDC_PWADLIST: + { + int i; + int count = 0; + int *selection = NULL; + int selectioncount = 0; + + SendMessage(launcher.listPWAD, LB_GETCOUNT, 0, 0); + + selectioncount = L_SelGetList(&selection); + + for (i=0; i < selectioncount; i++) + { + int index = selection[i]; + char *line = PathFindFileName(launcher.files[index].name); + int needlen = (tooltip_str?strlen(tooltip_str):0) + strlen(line) + sizeof(char) * 8; + if (needlen > tooltip_maxlen) + { + tooltip_str = realloc(tooltip_str, needlen); + tooltip_maxlen = needlen; + } + + if (count++ > 0) + strcat(strcat(tooltip_str, ", "), line); + else + strcpy(tooltip_str, line); + } + + free(selection); + + lpttt->lpszText = tooltip_str; + } + break; + } + } + return; +} + +static dboolean L_LauncherIsNeeded(void) +{ + int i; + dboolean pwad = false; + char *iwad = NULL; + +// SHIFT for invert +// if (GetAsyncKeyState(VK_SHIFT) ? launcher_enable : !launcher_enable) +// return false; + + if ((GetKeyState(VK_SHIFT) & 0x8000)) + return true; + + if (launcher_enable == launcher_enable_always) + return true; + + if (launcher_enable == launcher_enable_never) + return false; + + i = M_CheckParm("-iwad"); + if (i && (++i < myargc)) + iwad = I_FindFile(myargv[i], ".wad"); + + for (i=0; !pwad && i < (int)numwadfiles; i++) + pwad = wadfiles[i].src == source_pwad; + + return (!iwad && !pwad && !M_CheckParm("-auto")); +} + +void LauncherShow(unsigned int params) +{ + int result; + + if (!L_LauncherIsNeeded()) + return; + + launcher_params = params; + + InitCommonControls(); + sprintf(launchercachefile,"%s/"PACKAGE_TARNAME".cache", I_DoomExeDir()); + + result = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_LAUNCHERSERVERDIALOG), NULL, (DLGPROC)LauncherServerCallback); + + switch (result) + { + case 0: + I_SafeExit(-1); + break; + case 1: + M_SaveDefaults(); + break; + } +} + +#endif // _WIN32 diff --git a/src/e6y_launcher.h b/src/e6y_launcher.h new file mode 100644 index 0000000..6643784 --- /dev/null +++ b/src/e6y_launcher.h @@ -0,0 +1,55 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *----------------------------------------------------------------------------- + */ + +#ifndef __E6Y_LAUNCHER__ +#define __E6Y_LAUNCHER__ + +#ifdef _WIN32 + +typedef enum +{ + launcher_enable_never, + launcher_enable_smart, + launcher_enable_always, + + launcher_enable_count +} launcher_enable_t; +extern launcher_enable_t launcher_enable; +extern const char *launcher_enable_states[]; +extern char *launcher_history[10]; + +void LauncherShow(unsigned int params); + +#endif + +#endif diff --git a/src/f_finale.c b/src/f_finale.c new file mode 100644 index 0000000..f18b9cd --- /dev/null +++ b/src/f_finale.c @@ -0,0 +1,750 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Game completion, final screen animation. + * + *----------------------------------------------------------------------------- + */ + +#include "doomstat.h" +#include "d_event.h" +#include "g_game.h" +#include "v_video.h" +#include "w_wad.h" +#include "s_sound.h" +#include "sounds.h" +#include "d_deh.h" // Ty 03/22/98 - externalizations +#include "f_finale.h" // CPhipps - hmm... + + +// The implementation for UMAPINFO is kept separate to avoid demo sync issues +void FMI_Ticker (void); +void FMI_Drawer (void); +void FMI_StartFinale (void); +extern int using_FMI; + + +// Stage of animation: +// 0 = text, 1 = art screen, 2 = character cast +int finalestage; // cph - +int finalecount; // made static +const char* finaletext; // cph - +const char* finaleflat; // made static const + +// defines for the end mission display text // phares + +// CPhipps - removed the old finale screen text message strings; +// they were commented out for ages already +// Ty 03/22/98 - ... the new s_WHATEVER extern variables are used +// in the code below instead. + +void F_StartCast (void); +void F_CastTicker (void); +dboolean F_CastResponder (event_t *ev); +void F_CastDrawer (void); + +void WI_checkForAccelerate(void); // killough 3/28/98: used to +extern int acceleratestage; // accelerate intermission screens +int midstage; // whether we're in "mid-stage" + +// +// F_StartFinale +// +void F_StartFinale (void) +{ + dboolean mus_changed = false; + + gameaction = ga_nothing; + gamestate = GS_FINALE; + automapmode &= ~am_active; + + // killough 3/28/98: clear accelerative text flags + acceleratestage = midstage = 0; + + finaletext = NULL; + finaleflat = NULL; + + if (gamemapinfo && gamemapinfo->intermusic[0]) + { + int l = W_CheckNumForName(gamemapinfo->intermusic); + if (l >= 0) + { + S_ChangeMusInfoMusic(l, true); + mus_changed = true; + } + } + + // Okay - IWAD dependend stuff. + // This has been changed severly, and + // some stuff might have changed in the process. + switch ( gamemode ) + { + // DOOM 1 - E1, E3 or E4, but each nine missions + case shareware: + case registered: + case retail: + { + if (!mus_changed) S_ChangeMusic(mus_victor, true); + + switch (gameepisode) + { + case 1: + finaleflat = bgflatE1; // Ty 03/30/98 - new externalized bg flats + finaletext = s_E1TEXT; // Ty 03/23/98 - Was e1text variable. + break; + case 2: + finaleflat = bgflatE2; + finaletext = s_E2TEXT; // Ty 03/23/98 - Same stuff for each + break; + case 3: + finaleflat = bgflatE3; + finaletext = s_E3TEXT; + break; + case 4: + finaleflat = bgflatE4; + finaletext = s_E4TEXT; + break; + default: + // Ouch. + break; + } + break; + } + + // DOOM II and missions packs with E1, M34 + case commercial: + { + if (!mus_changed) S_ChangeMusic(mus_read_m, true); + + // Ty 08/27/98 - added the gamemission logic + switch (gamemap) + { + case 6: + finaleflat = bgflat06; + finaletext = (gamemission==pack_tnt) ? s_T1TEXT : + (gamemission==pack_plut) ? s_P1TEXT : s_C1TEXT; + break; + case 11: + finaleflat = bgflat11; + finaletext = (gamemission==pack_tnt) ? s_T2TEXT : + (gamemission==pack_plut) ? s_P2TEXT : s_C2TEXT; + break; + case 20: + finaleflat = bgflat20; + finaletext = (gamemission==pack_tnt) ? s_T3TEXT : + (gamemission==pack_plut) ? s_P3TEXT : s_C3TEXT; + break; + case 30: + finaleflat = bgflat30; + finaletext = (gamemission==pack_tnt) ? s_T4TEXT : + (gamemission==pack_plut) ? s_P4TEXT : s_C4TEXT; + break; + case 15: + finaleflat = bgflat15; + finaletext = (gamemission==pack_tnt) ? s_T5TEXT : + (gamemission==pack_plut) ? s_P5TEXT : s_C5TEXT; + break; + case 31: + finaleflat = bgflat31; + finaletext = (gamemission==pack_tnt) ? s_T6TEXT : + (gamemission==pack_plut) ? s_P6TEXT : s_C6TEXT; + break; + default: + // Ouch. + break; + } + if (gamemission == pack_nerve && gamemap == 8) + { + finaleflat = bgflat06; + finaletext = s_C6TEXT; + } + break; + // Ty 08/27/98 - end gamemission logic + } + + // Indeterminate. + default: // Ty 03/30/98 - not externalized + if (!mus_changed) S_ChangeMusic(mus_read_m, true); + finaleflat = "F_SKY1"; // Not used anywhere else. + finaletext = s_C1TEXT; // FIXME - other text, music? + break; + } + + using_FMI = false; + + if (gamemapinfo) + { + FMI_StartFinale(); + } + + finalestage = 0; + finalecount = 0; +} + + + +dboolean F_Responder (event_t *event) +{ + if (finalestage == 2) + return F_CastResponder (event); + + return false; +} + +// Get_TextSpeed() returns the value of the text display speed // phares +// Rewritten to allow user-directed acceleration -- killough 3/28/98 + +float Get_TextSpeed(void) +{ + return midstage ? NEWTEXTSPEED : (midstage=acceleratestage) ? + acceleratestage=0, NEWTEXTSPEED : TEXTSPEED; +} + + +// +// F_Ticker +// +// killough 3/28/98: almost totally rewritten, to use +// player-directed acceleration instead of constant delays. +// Now the player can accelerate the text display by using +// the fire/use keys while it is being printed. The delay +// automatically responds to the user, and gives enough +// time to read. +// +// killough 5/10/98: add back v1.9 demo compatibility +// + +void F_Ticker(void) +{ + int i; + + if (using_FMI) + { + FMI_Ticker(); + return; + } + + if (!demo_compatibility) + WI_checkForAccelerate(); // killough 3/28/98: check for acceleration + else + if (gamemode == commercial && finalecount > 50) // check for skipping + for (i=0; i strlen(finaletext)*speed + + (midstage ? NEWTEXTWAIT : TEXTWAIT) || + (midstage && acceleratestage)) { + if (gamemode != commercial) // Doom 1 / Ultimate Doom episode end + { // with enough time, it's automatic + finalecount = 0; + finalestage = 1; + wipegamestate = -1; // force a wipe + if (gameepisode == 3) + S_StartMusic(mus_bunny); + } + else // you must press a button to continue in Doom 2 + if (!demo_compatibility && midstage) + { + next_level: + if (gamemap == 30 || (gamemission == pack_nerve && singleplayer && gamemap == 8)) + F_StartCast(); // cast of Doom 2 characters + else + gameaction = ga_worlddone; // next level, e.g. MAP07 + } + } + } +} + +// +// F_TextWrite +// +// This program displays the background and text at end-mission // phares +// text time. It draws both repeatedly so that other displays, // | +// like the main menu, can be drawn over it dynamically and // V +// erased dynamically. The TEXTSPEED constant is changed into +// the Get_TextSpeed function so that the speed of writing the // ^ +// text can be increased, and there's still time to read what's // | +// written. // phares +// CPhipps - reformatted + +#include "hu_stuff.h" +extern patchnum_t hu_font[HU_FONTSIZE]; + + +void F_TextWrite (void) +{ + // [FG] if interbackdrop does not specify a valid flat, draw it as a patch instead + if (gamemapinfo && W_CheckNumForName(finaleflat) != -1 && + (W_CheckNumForName)(finaleflat, ns_flats) == -1) + { + V_FillBorder(-1, 0); + V_DrawNamePatch(0, 0, 0, finaleflat, CR_DEFAULT, VPT_STRETCH); + } + else + V_DrawBackground(finaleflat, 0); + { // draw some of the text onto the screen + int cx = 10; + int cy = 10; + const char* ch = finaletext; // CPhipps - const + int count = (int)((float)(finalecount - 10)/Get_TextSpeed()); // phares + int w; + + if (count < 0) + count = 0; + + for ( ; count ; count-- ) { + int 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 = hu_font[c].width; + if (cx+w > SCREENWIDTH) + break; + // CPhipps - patch drawing updated + V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH); + cx+=w; + } + } +} + +// +// Final DOOM 2 animation +// Casting by id Software. +// in order of appearance +// +typedef struct +{ + const char **name; // CPhipps - const** + mobjtype_t type; +} castinfo_t; + +static const castinfo_t castorder[] = { // CPhipps - static const, initialised here + { &s_CC_ZOMBIE, MT_POSSESSED }, + { &s_CC_SHOTGUN, MT_SHOTGUY }, + { &s_CC_HEAVY, MT_CHAINGUY }, + { &s_CC_IMP, MT_TROOP }, + { &s_CC_DEMON, MT_SERGEANT }, + { &s_CC_LOST, MT_SKULL }, + { &s_CC_CACO, MT_HEAD }, + { &s_CC_HELL, MT_KNIGHT }, + { &s_CC_BARON, MT_BRUISER }, + { &s_CC_ARACH, MT_BABY }, + { &s_CC_PAIN, MT_PAIN }, + { &s_CC_REVEN, MT_UNDEAD }, + { &s_CC_MANCU, MT_FATSO }, + { &s_CC_ARCH, MT_VILE }, + { &s_CC_SPIDER, MT_SPIDER }, + { &s_CC_CYBER, MT_CYBORG }, + { &s_CC_HERO, MT_PLAYER }, + { NULL, 0} + }; + +int castnum; +int casttics; +state_t* caststate; +dboolean castdeath; +int castframes; +int castonmelee; +dboolean castattacking; + + +// +// F_StartCast +// + +void F_StartCast (void) +{ + wipegamestate = -1; // force a screen wipe + castnum = 0; + caststate = &states[mobjinfo[castorder[castnum].type].seestate]; + casttics = caststate->tics; + castdeath = false; + finalestage = 2; + castframes = 0; + castonmelee = 0; + castattacking = false; + S_ChangeMusic(mus_evil, true); +} + + +// +// F_CastTicker +// +void F_CastTicker (void) +{ + int st; + int sfx; + + if (--casttics > 0) + return; // not time to change state yet + + if (caststate->tics == -1 || caststate->nextstate == S_NULL) + { + // switch from deathstate to next monster + castnum++; + castdeath = false; + if (castorder[castnum].name == NULL) + castnum = 0; + if (mobjinfo[castorder[castnum].type].seesound) + S_StartSound (NULL, mobjinfo[castorder[castnum].type].seesound); + caststate = &states[mobjinfo[castorder[castnum].type].seestate]; + castframes = 0; + } + else + { + // just advance to next state in animation + if (caststate == &states[S_PLAY_ATK1]) + goto stopattack; // Oh, gross hack! + st = caststate->nextstate; + caststate = &states[st]; + castframes++; + + // sound hacks.... + switch (st) + { + case S_PLAY_ATK1: sfx = sfx_dshtgn; break; + case S_POSS_ATK2: sfx = sfx_pistol; break; + case S_SPOS_ATK2: sfx = sfx_shotgn; break; + case S_VILE_ATK2: sfx = sfx_vilatk; break; + case S_SKEL_FIST2: sfx = sfx_skeswg; break; + case S_SKEL_FIST4: sfx = sfx_skepch; break; + case S_SKEL_MISS2: sfx = sfx_skeatk; break; + case S_FATT_ATK8: + case S_FATT_ATK5: + case S_FATT_ATK2: sfx = sfx_firsht; break; + case S_CPOS_ATK2: + case S_CPOS_ATK3: + case S_CPOS_ATK4: sfx = sfx_shotgn; break; + case S_TROO_ATK3: sfx = sfx_claw; break; + case S_SARG_ATK2: sfx = sfx_sgtatk; break; + case S_BOSS_ATK2: + case S_BOS2_ATK2: + case S_HEAD_ATK2: sfx = sfx_firsht; break; + case S_SKULL_ATK2: sfx = sfx_sklatk; break; + case S_SPID_ATK2: + case S_SPID_ATK3: sfx = sfx_shotgn; break; + case S_BSPI_ATK2: sfx = sfx_plasma; break; + case S_CYBER_ATK2: + case S_CYBER_ATK4: + case S_CYBER_ATK6: sfx = sfx_rlaunc; break; + case S_PAIN_ATK3: sfx = sfx_sklatk; break; + default: sfx = 0; break; + } + + if (sfx) + S_StartSound (NULL, sfx); + } + + if (castframes == 12) + { + // go into attack frame + castattacking = true; + if (castonmelee) + caststate=&states[mobjinfo[castorder[castnum].type].meleestate]; + else + caststate=&states[mobjinfo[castorder[castnum].type].missilestate]; + castonmelee ^= 1; + if (caststate == &states[S_NULL]) + { + if (castonmelee) + caststate= + &states[mobjinfo[castorder[castnum].type].meleestate]; + else + caststate= + &states[mobjinfo[castorder[castnum].type].missilestate]; + } + } + + if (castattacking) + { + if (castframes == 24 + || caststate == &states[mobjinfo[castorder[castnum].type].seestate] ) + { + stopattack: + castattacking = false; + castframes = 0; + caststate = &states[mobjinfo[castorder[castnum].type].seestate]; + } + } + + casttics = caststate->tics; + if (casttics == -1) + casttics = 15; +} + + +// +// F_CastResponder +// + +dboolean F_CastResponder (event_t* ev) +{ + if (ev->type != ev_keydown) + return false; + + if (castdeath) + return true; // already in dying frames + + // go into death frame + castdeath = true; + caststate = &states[mobjinfo[castorder[castnum].type].deathstate]; + casttics = caststate->tics; + castframes = 0; + castattacking = false; + if (mobjinfo[castorder[castnum].type].deathsound) + S_StartSound (NULL, mobjinfo[castorder[castnum].type].deathsound); + + return true; +} + + +static void F_CastPrint (const char* text) // CPhipps - static, const char* +{ + const char* ch; // CPhipps - const + 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 = hu_font[c].width; + width += w; + } + + // draw it + cx = 160-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 = hu_font[c].width; + // CPhipps - patch drawing updated + V_DrawNumPatch(cx, 180, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH); + cx+=w; + } +} + + +// +// F_CastDrawer +// + +void F_CastDrawer (void) +{ + spritedef_t* sprdef; + spriteframe_t* sprframe; + int lump; + dboolean flip; + + // e6y: wide-res + V_FillBorder(-1, 0); + // erase the entire screen to a background + // CPhipps - patch drawing updated + V_DrawNamePatch(0,0,0, bgcastcall, CR_DEFAULT, VPT_STRETCH); // Ty 03/30/98 bg texture extern + + F_CastPrint (*(castorder[castnum].name)); + + // draw the current frame in the middle of the screen + sprdef = &sprites[caststate->sprite]; + sprframe = &sprdef->spriteframes[ caststate->frame & FF_FRAMEMASK]; + lump = sprframe->lump[0]; + flip = (dboolean)(sprframe->flip & 1); + + // CPhipps - patch drawing updated + V_DrawNumPatch(160, 170, 0, lump+firstspritelump, CR_DEFAULT, + VPT_STRETCH | (flip ? VPT_FLIP : 0)); +} + +// +// F_BunnyScroll +// +static const char pfub2[] = { "PFUB2" }; +static const char pfub1[] = { "PFUB1" }; + +void F_BunnyScroll (void) +{ + char name[10]; + int stage; + static int laststage; + static int p1offset, p2width; + + if (finalecount == 0) + { + const rpatch_t *p1, *p2; + p1 = R_CachePatchName(pfub1); + p2 = R_CachePatchName(pfub2); + + p2width = p2->width; + if (p1->width == 320) + { + // Unity or original PFUBs. + p1offset = (p2width - 320) / 2; + } + else + { + // Widescreen mod PFUBs. + p1offset = 0; + } + + W_UnlockLumpName(pfub2); + W_UnlockLumpName(pfub1); + } + + { + int scrolled = 320 - (finalecount-230)/2; + if (scrolled <= 0) { + V_DrawNamePatch(0, 0, 0, pfub2, CR_DEFAULT, VPT_STRETCH); + } else if (scrolled >= 320) { + V_DrawNamePatch(p1offset, 0, 0, pfub1, CR_DEFAULT, VPT_STRETCH); + if (p1offset > 0) + V_DrawNamePatch(-320, 0, 0, pfub2, CR_DEFAULT, VPT_STRETCH); + } else { + V_DrawNamePatch(p1offset+320-scrolled, 0, 0, pfub1, CR_DEFAULT, VPT_STRETCH); + V_DrawNamePatch(-scrolled, 0, 0, pfub2, CR_DEFAULT, VPT_STRETCH); + } + if (p2width == 320) + V_FillBorder(-1, 0); + } + + if (finalecount < 1130) + return; + if (finalecount < 1180) + { + // CPhipps - patch drawing updated + V_DrawNamePatch((320-13*8)/2, (200-8*8)/2,0, "END0", CR_DEFAULT, VPT_STRETCH); + laststage = 0; + return; + } + + stage = (finalecount-1180) / 5; + if (stage > 6) + stage = 6; + if (stage > laststage) + { + S_StartSound (NULL, sfx_pistol); + laststage = stage; + } + + sprintf (name,"END%i",stage); + // CPhipps - patch drawing updated + V_DrawNamePatch((320-13*8)/2, (200-8*8)/2, 0, name, CR_DEFAULT, VPT_STRETCH); +} + + +// +// F_Drawer +// +void F_Drawer (void) +{ + if (using_FMI) + { + FMI_Drawer(); + return; + } + + if (finalestage == 2) + { + F_CastDrawer (); + return; + } + + if (!finalestage) + F_TextWrite (); + else + { + // e6y: wide-res + V_FillBorder(-1, 0); + switch (gameepisode) + { + // CPhipps - patch drawing updated + case 1: + if ( gamemode == retail || gamemode == commercial ) + V_DrawNamePatch(0, 0, 0, "CREDIT", CR_DEFAULT, VPT_STRETCH); + else + V_DrawNamePatch(0, 0, 0, "HELP2", CR_DEFAULT, VPT_STRETCH); + break; + case 2: + V_DrawNamePatch(0, 0, 0, "VICTORY2", CR_DEFAULT, VPT_STRETCH); + break; + case 3: + F_BunnyScroll (); + break; + case 4: + V_DrawNamePatch(0, 0, 0, "ENDPIC", CR_DEFAULT, VPT_STRETCH); + break; + } + } +} diff --git a/src/f_finale.h b/src/f_finale.h new file mode 100644 index 0000000..855f4aa --- /dev/null +++ b/src/f_finale.h @@ -0,0 +1,72 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Related to f_finale.c, which is called at the end of a level + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __F_FINALE__ +#define __F_FINALE__ + +#include "doomtype.h" +#include "d_event.h" + +/* + * FINALE + */ + + // Stage of animation: +// 0 = text, 1 = art screen, 2 = character cast +extern int finalestage; // cph - +extern int finalecount; // made static +extern const char* finaletext; // cph - +extern const char* finaleflat; // made static const + +// defines for the end mission display text // phares + +#define TEXTSPEED 3 // original value // phares +#define TEXTWAIT 250 // original value // phares +#define NEWTEXTSPEED 0.01f // new value // phares +#define NEWTEXTWAIT 1000 // new value // phares + + + +/* Called by main loop. */ +dboolean F_Responder (event_t* ev); + +/* Called by main loop. */ +void F_Ticker (void); + +/* Called by main loop. */ +void F_Drawer (void); + +void F_StartFinale (void); + +#endif diff --git a/src/f_finale2.c b/src/f_finale2.c new file mode 100644 index 0000000..57ea4ac --- /dev/null +++ b/src/f_finale2.c @@ -0,0 +1,172 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * Copyright 2017 by + * Christoph Oelckers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Game completion, final screen animation. + * This is an alternative version for UMAPINFO defined + * intermission so that the feature can be cleanly implemented + * without worrying about demo sync implications + * + *----------------------------------------------------------------------------- + */ + +#include "doomstat.h" +#include "d_event.h" +#include "g_game.h" +#include "v_video.h" +#include "w_wad.h" +#include "s_sound.h" +#include "sounds.h" +#include "d_deh.h" // Ty 03/22/98 - externalizations +#include "f_finale.h" // CPhipps - hmm... + +void F_StartCast (void); +void F_TextWrite(void); +void F_BunnyScroll(void); + +void WI_checkForAccelerate(void); // killough 3/28/98: used to +float Get_TextSpeed(void); +extern int acceleratestage; // accelerate intermission screens +extern int midstage; +int using_FMI; + +// +// F_StartFinale +// +extern dboolean secretexit; + +void FMI_StartFinale(void) +{ + if (gamemapinfo->intertextsecret && secretexit && gamemapinfo->intertextsecret[0] != '-') // '-' means that any default intermission was cleared. + { + finaletext = gamemapinfo->intertextsecret; + } + else if (gamemapinfo->intertext && !secretexit && gamemapinfo->intertext[0] != '-') // '-' means that any default intermission was cleared. + { + finaletext = gamemapinfo->intertext; + } + + if (!finaletext) finaletext = "The End"; // this is to avoid a crash on a missing text in the last map. + + if (gamemapinfo->interbackdrop[0]) + { + finaleflat = gamemapinfo->interbackdrop; + } + + if (!finaleflat) finaleflat = "FLOOR4_8"; // use a single fallback for all maps. + + using_FMI = true; +} + + + +// +// F_Ticker +// +// killough 3/28/98: almost totally rewritten, to use +// player-directed acceleration instead of constant delays. +// Now the player can accelerate the text display by using +// the fire/use keys while it is being printed. The delay +// automatically responds to the user, and gives enough +// time to read. +// +// killough 5/10/98: add back v1.9 demo compatibility +// + + +void FMI_Ticker(void) +{ + int i; + if (!demo_compatibility) WI_checkForAccelerate(); // killough 3/28/98: check for acceleration + else for (i = 0; i < MAXPLAYERS; i++) if (players[i].cmd.buttons) goto next_level; // go on to the next level + + // advance animation + finalecount++; + + if (!finalestage) + { + float speed = demo_compatibility ? TEXTSPEED : Get_TextSpeed(); + /* killough 2/28/98: changed to allow acceleration */ + if (finalecount > strlen(finaletext)*speed + + (midstage ? NEWTEXTWAIT : TEXTWAIT) || + (midstage && acceleratestage)) { + + next_level: + if (gamemapinfo->endpic[0] && (strcmp(gamemapinfo->endpic, "-") != 0)) + { + if (!stricmp(gamemapinfo->endpic, "$CAST")) + { + F_StartCast(); + using_FMI = false; + } + else + { + finalecount = 0; + finalestage = 1; + wipegamestate = -1; // force a wipe + if (!stricmp(gamemapinfo->endpic, "$BUNNY")) + { + S_StartMusic(mus_bunny); + } + else if (!stricmp(gamemapinfo->endpic, "!")) + { + using_FMI = false; + } + } + } + else + { + gameaction = ga_worlddone; // next level, e.g. MAP07 + } + } + } +} + + +// +// F_Drawer +// +void FMI_Drawer(void) +{ + if (!finalestage || !gamemapinfo->endpic[0] || (strcmp(gamemapinfo->endpic, "-") == 0)) + { + F_TextWrite(); + } + else if (strcmp(gamemapinfo->endpic, "$BUNNY") == 0) + { + F_BunnyScroll(); + } + else + { + // e6y: wide-res + V_FillBorder(-1, 0); + V_DrawNamePatch(0, 0, 0, gamemapinfo->endpic, CR_DEFAULT, VPT_STRETCH); + } +} diff --git a/src/f_wipe.c b/src/f_wipe.c new file mode 100644 index 0000000..4fe66f4 --- /dev/null +++ b/src/f_wipe.c @@ -0,0 +1,268 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Mission begin melt/wipe screen special effect. + * + *----------------------------------------------------------------------------- + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "z_zone.h" +#include "doomdef.h" +#include "i_video.h" +#include "v_video.h" +#include "m_random.h" +#include "f_wipe.h" +#ifdef GL_DOOM +#include "gl_struct.h" +#endif +#include "e6y.h"//e6y + +// +// SCREEN WIPE PACKAGE +// + +// Parts re-written to support true-color video modes. Column-major +// formatting removed. - POPE + +// CPhipps - macros for the source and destination screens +#define SRC_SCR 2 +#define DEST_SCR 3 + +static screeninfo_t wipe_scr_start; +static screeninfo_t wipe_scr_end; +static screeninfo_t wipe_scr; + +// e6y: resolution limitation is removed +static int *y_lookup = NULL; + +// e6y: resolution limitation is removed +void R_InitMeltRes(void) +{ + if (y_lookup) free(y_lookup); + + y_lookup = calloc(1, SCREENWIDTH * sizeof(*y_lookup)); +} + +static int wipe_initMelt(int ticks) +{ + int i; + + if (V_GetMode() != VID_MODEGL) + { + // copy start screen to main screen + for(i=0;i not ready to scroll yet) + y_lookup[0] = -(M_Random()%16); + for (i=1;i 0) + y_lookup[i] = 0; + else + if (y_lookup[i] == -16) + y_lookup[i] = -15; + } + return 0; +} + +static int wipe_doMelt(int ticks) +{ + dboolean done = true; + int i; + const int depth = V_GetPixelDepth(); + + while (ticks--) { + for (i=0;i<(SCREENWIDTH);i++) { + if (y_lookup[i]<0) { + y_lookup[i]++; + done = false; + continue; + } + if (y_lookup[i] < SCREENHEIGHT) { + byte *s, *d; + int j, k, dy; + + /* cph 2001/07/29 - + * The original melt rate was 8 pixels/sec, i.e. 25 frames to melt + * the whole screen, so make the melt rate depend on SCREENHEIGHT + * so it takes no longer in high res + */ + dy = (y_lookup[i] < 16) ? y_lookup[i]+1 : SCREENHEIGHT/25; + if (y_lookup[i]+dy >= SCREENHEIGHT) + dy = SCREENHEIGHT - y_lookup[i]; + + if (V_GetMode() != VID_MODEGL) { + s = wipe_scr_end.data + (y_lookup[i]*wipe_scr_end.byte_pitch+(i*depth)); + d = wipe_scr.data + (y_lookup[i]*wipe_scr.byte_pitch+(i*depth)); + for (j=dy;j;j--) { + for (k=0; k +#include +#include +#ifdef _MSC_VER +#include +#else +#include +#endif +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomstat.h" +#include "d_net.h" +#include "f_finale.h" +#include "m_argv.h" +#include "m_misc.h" +#include "m_menu.h" +#include "m_random.h" +#include "p_setup.h" +#include "p_saveg.h" +#include "p_tick.h" +#include "p_map.h" +#include "p_checksum.h" +#include "d_main.h" +#include "wi_stuff.h" +#include "hu_stuff.h" +#include "st_stuff.h" +#include "am_map.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_draw.h" +#include "p_map.h" +#include "s_sound.h" +#include "s_advsound.h" +#include "dstrings.h" +#include "sounds.h" +#include "r_data.h" +#include "r_sky.h" +#include "d_deh.h" // Ty 3/27/98 deh declarations +#include "p_inter.h" +#include "g_game.h" +#include "lprintf.h" +#include "i_main.h" +#include "i_system.h" +#include "r_demo.h" +#include "r_fps.h" +#include "e6y.h"//e6y +#include "statdump.h" + +#include "m_io.h" + +// ano - used for version 255+ demos, like EE or MBF +static char prdemosig[] = "PR+UM"; + +#define SAVEGAMESIZE 0x20000 +#define SAVESTRINGSIZE 24 + +struct MapEntry *G_LookupMapinfo(int gameepisode, int gamemap); + +// e6y +// It is signature for new savegame format with continuous numbering. +// Now it is not necessary to add a new level of compatibility in case +// of need to savegame format change from one minor version to another. +// The old format is still supported. +#define NEWFORMATSIG "\xff\xff\xff\xff" + +static size_t savegamesize = SAVEGAMESIZE; // killough +static dboolean netdemo; +static const byte *demobuffer; /* cph - only used for playback */ +static int demolength; // check for overrun (missing DEMOMARKER) +static FILE *demofp; /* cph - record straight to file */ +//e6y static +const byte *demo_p; +const byte *demo_continue_p = NULL; +static short consistancy[MAXPLAYERS][BACKUPTICS]; + +gameaction_t gameaction; +gamestate_t gamestate; +skill_t gameskill; +dboolean respawnmonsters; +int gameepisode; +int gamemap; +struct MapEntry *gamemapinfo; +dboolean paused; +// CPhipps - moved *_loadgame vars here +static dboolean forced_loadgame = false; +static dboolean command_loadgame = false; + +dboolean usergame; // ok to save / end game +dboolean timingdemo; // if true, exit with report on completion +dboolean fastdemo; // if true, run at full speed -- killough +dboolean nodrawers; // for comparative timing purposes +dboolean noblit; // for comparative timing purposes +int starttime; // for comparative timing purposes +dboolean deathmatch; // only if started as net death +dboolean netgame; // only true if packets are broadcast +dboolean playeringame[MAXPLAYERS]; +player_t players[MAXPLAYERS]; +int upmove; +int consoleplayer; // player taking events and displaying +int displayplayer; // view being displayed +int gametic; +int basetic; /* killough 9/29/98: for demo sync */ +int totalkills, totallive, totalitems, totalsecret; // for intermission +int show_alive; +dboolean demorecording; +dboolean demoplayback; +dboolean democontinue = false; +char democontinuename[PATH_MAX]; +char* demo_filename = NULL; +dboolean singledemo; // quit after playing a demo from cmdline +wbstartstruct_t wminfo; // parms for world map / intermission +dboolean haswolflevels = false;// jff 4/18/98 wolf levels present +static byte *savebuffer; // CPhipps - static +int autorun = false; // always running? // phares +int totalleveltimes; // CPhipps - total time for all completed levels +int longtics; +int bytes_per_tic; + +dboolean boom_autoswitch; +dboolean done_autoswitch; + +dboolean coop_spawns; + +// e6y +// There is a new command-line switch "-shorttics". +// This makes it possible to practice routes and tricks +// (e.g. glides, where this makes a significant difference) +// with the same mouse behaviour as when recording, +// but without having to be recording every time. +int shorttics; + +// automatic pistol start when advancing from one level to the next +int pistolstart; + +// +// controls (have defaults) +// + +int key_right; +int key_left; +int key_up; +int key_down; +int key_mlook; +int key_novert; +int key_menu_right; // phares 3/7/98 +int key_menu_left; // | +int key_menu_up; // V +int key_menu_down; +int key_menu_backspace; // ^ +int key_menu_escape; // | +int key_menu_enter; // phares 3/7/98 +int key_menu_clear; +int key_strafeleft; +int key_straferight; +int key_flyup; +int key_flydown; +int key_fire; +int key_use; +int key_strafe; +int key_speed; +int key_escape = KEYD_ESCAPE; // phares 4/13/98 +int key_savegame; // phares +int key_loadgame; // | +int key_autorun; // V +int key_reverse; +int key_zoomin; +int key_zoomout; +int key_chat; +int key_backspace; +int key_enter; +int key_map_right; +int key_map_left; +int key_map_up; +int key_map_down; +int key_map_zoomin; +int key_map_zoomout; +int key_map; +int key_map_gobig; +int key_map_follow; +int key_map_mark; +int key_map_clear; +int key_map_grid; +int key_map_overlay; // cph - map overlay +int key_map_rotate; // cph - map rotation +int key_map_textured; // e6y - textured automap +int key_help = KEYD_F1; // phares 4/13/98 +int key_soundvolume; +int key_hud; +int key_quicksave; +int key_endgame; +int key_messages; +int key_quickload; +int key_quit; +int key_gamma; +int key_spy; +int key_pause; +int key_setup; +int destination_keys[MAXPLAYERS]; +int key_weapontoggle; +int key_weapon1; +int key_weapon2; +int key_weapon3; +int key_weapon4; +int key_weapon5; +int key_weapon6; +int key_weapon7; // ^ +int key_weapon8; // | +int key_weapon9; // phares +int key_nextweapon; +int key_prevweapon; + +int mb_weapon1; +int mb_weapon2; +int mb_weapon3; +int mb_weapon4; +int mb_weapon5; +int mb_weapon6; +int mb_weapon7; +int mb_weapon8; +int mb_weapon9; + +int key_screenshot; // killough 2/22/98: screenshot key +int mousebfire; +int mousebstrafe; +int mousebforward; +int mousebbackward; +int mousebturnright; +int mousebturnleft; +int mousebuse; +int mousebspeed; +int joybfire; +int joybstrafe; +int joybstrafeleft; +int joybstraferight; +int joybuse; +int joybspeed; + +#define MAXPLMOVE (forwardmove[1]) +#define TURBOTHRESHOLD 0x32 +#define SLOWTURNTICS 6 +#define QUICKREVERSE (short)32768 // 180 degree reverse // phares +#define NUMKEYS 512 + +fixed_t forwardmove[2] = {0x19, 0x32}; +fixed_t sidemove[2] = {0x18, 0x28}; +fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn +fixed_t flyspeed[2] = {1*256, 3*256}; + +fixed_t forwardmove_normal[2] = {0x19, 0x32}; +fixed_t sidemove_normal[2] = {0x18, 0x28}; +fixed_t sidemove_strafe50[2] = {0x19, 0x32}; + +// CPhipps - made lots of key/button state vars static +//e6y static +dboolean gamekeydown[NUMKEYS]; +static int turnheld; // for accelerative turning + +// 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 +{ + weapontype_t weapon; + weapontype_t weapon_num; +} 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 int mousearray[MAX_MOUSEB + 1]; +static int *mousebuttons = &mousearray[1]; // allow [-1] + +// mouse values are used once +static int mousex; +static int mousey; +static int dclicktime; +static int dclickstate; +static int dclicks; +static int dclicktime2; +static int dclickstate2; +static int dclicks2; + +// joystick values are repeated +static int joyxmove; +static int joyymove; +static dboolean joyarray[9]; +static dboolean *joybuttons = &joyarray[1]; // allow [-1] + +// Game events info +static buttoncode_t special_event; // Event triggered by local player, to send +static byte savegameslot; // Slot to load if gameaction == ga_loadgame +char savedescription[SAVEDESCLEN]; // Description to save in savegame if gameaction == ga_savegame + +//jff 3/24/98 define defaultskill here +int defaultskill; //note 1-based + +// killough 2/8/98: make corpse queue variable in size +int bodyqueslot, bodyquesize; // killough 2/8/98 +mobj_t **bodyque = 0; // phares 8/10/98 + +static void G_DoSaveGame (dboolean menu); + +//e6y: save/restore all data which could be changed by G_ReadDemoHeader +static void G_SaveRestoreGameOptions(int save); + +// +// G_BuildTiccmd +// Builds a ticcmd from all of the available inputs +// or reads it from the demo buffer. +// If recording a demo, write it out +// +static inline signed char fudgef(signed char b) +{ +/*e6y + static int c; + if (!b || !demo_compatibility || longtics) return b; + if (++c & 0x1f) return b; + b |= 1; if (b>2) b-=2;*/ + return b; +} + +static void SetMouseButtons(unsigned int buttons_mask) +{ + int i; + + for (i = 0; i < MAX_MOUSEB; ++i) + { + unsigned int button_on = (buttons_mask & (1 << i)) != 0; + mousebuttons[i] = button_on; + } +} + +void G_SetSpeed(void) +{ + int p; + + if(movement_strafe50) + { + sidemove[0] = sidemove_strafe50[0]; + sidemove[1] = sidemove_strafe50[1]; + } + else + { + sidemove[0] = sidemove_normal[0]; + sidemove[1] = sidemove_normal[1]; + } + + if ((p = M_CheckParm ("-turbo"))) + { + int scale = ((p < myargc - 1) ? atoi(myargv[p + 1]) : 200); + scale = BETWEEN(10, 400, scale); + + //jff 9/3/98 use logical output routine + lprintf (LO_CONFIRM,"turbo scale: %i%%\n",scale); + forwardmove[0] = forwardmove_normal[0]*scale/100; + forwardmove[1] = forwardmove_normal[1]*scale/100; + sidemove[0] = sidemove[0]*scale/100; + sidemove[1] = sidemove[1]*scale/100; + } +} + +static dboolean WeaponSelectable(weapontype_t weapon) +{ + if (gamemode == shareware) + { + if (weapon == wp_plasma || weapon == wp_bfg) + return false; + } + + // Can't select the super shotgun in Doom 1. + if (weapon == wp_supershotgun && gamemission == doom) + { + return false; + } + + // Can't select a weapon if we don't own it. + if (!players[consoleplayer].weaponowned[weapon]) + { + return false; + } + + return true; +} + +static int G_NextWeapon(int direction) +{ + weapontype_t weapon; + int start_i, i, arrlen; + + // Find index in the table. + if (players[consoleplayer].pendingweapon == wp_nochange) + { + weapon = players[consoleplayer].readyweapon; + } + else + { + weapon = players[consoleplayer].pendingweapon; + } + + arrlen = sizeof(weapon_order_table) / sizeof(*weapon_order_table); + for (i = 0; i < arrlen; i++) + { + if (weapon_order_table[i].weapon == weapon) + { + break; + } + } + + // Switch weapon. Don't loop forever. + start_i = i; + do + { + i += direction; + i = (i + arrlen) % arrlen; + } + while (i != start_i && !WeaponSelectable(weapon_order_table[i].weapon)); + + return weapon_order_table[i].weapon_num; +} + +void G_BuildTiccmd(ticcmd_t* cmd) +{ + int strafe; + int bstrafe; + int speed; + int tspeed; + int forward; + int side; + int newweapon; // phares + /* cphipps - remove needless I_BaseTiccmd call, just set the ticcmd to zero */ + memset(cmd,0,sizeof*cmd); + cmd->consistancy = consistancy[consoleplayer][maketic%BACKUPTICS]; + + strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] + || joybuttons[joybstrafe]; + //e6y: the "RUN" key inverts the autorun state + speed = (gamekeydown[key_speed] || joybuttons[joybspeed] || mousebuttons[mousebspeed] ? !autorun : autorun); // phares + + forward = side = 0; + + G_SkipDemoCheck(); //e6y + + if (democontinue) + { + mousex = mousey = 0; + return; + } + + // 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; + + // turn 180 degrees in one keystroke? // phares + // | + if (gamekeydown[key_reverse]) // V + { + if (!strafe) + cmd->angleturn += QUICKREVERSE; // ^ + gamekeydown[key_reverse] = false; // | + } // phares + + // 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 (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 (joyxmove > 0) + cmd->angleturn -= angleturn[tspeed]; + if (joyxmove < 0) + cmd->angleturn += angleturn[tspeed]; + } + + if (gamekeydown[key_up]) + forward += forwardmove[speed]; + if (gamekeydown[key_down]) + forward -= forwardmove[speed]; + if (joyymove < 0) + forward += forwardmove[speed]; + if (joyymove > 0) + forward -= forwardmove[speed]; + if (gamekeydown[key_straferight] || joybuttons[joybstraferight]) + side += sidemove[speed]; + if (gamekeydown[key_strafeleft] || joybuttons[joybstrafeleft]) + side -= sidemove[speed]; + + // buttons + cmd->chatchar = HU_dequeueChatChar(); + + if (gamekeydown[key_fire] || mousebuttons[mousebfire] || + joybuttons[joybfire]) + cmd->buttons |= BT_ATTACK; + + if (gamekeydown[key_use] || mousebuttons[mousebuse] || + joybuttons[joybuse]) + { + cmd->buttons |= BT_USE; + // clear double clicks if hit use button + dclicks = 0; + } + + // Toggle between the top 2 favorite weapons. // phares + // If not currently aiming one of these, switch to // phares + // the favorite. Only switch if you possess the weapon. // phares + + // killough 3/22/98: + // + // Perform automatic weapons switch here rather than in p_pspr.c, + // except in demo_compatibility mode. + // + // killough 3/26/98, 4/2/98: fix autoswitch when no weapons are left + + // Make Boom insert only a single weapon change command on autoswitch. + if ((!demo_compatibility && players[consoleplayer].attackdown && // killough + !P_CheckAmmo(&players[consoleplayer]) && !(done_autoswitch && boom_autoswitch)) || + gamekeydown[key_weapontoggle]) + { + newweapon = P_SwitchWeapon(&players[consoleplayer]); // phares + done_autoswitch = true; + } + else + { // phares 02/26/98: Added gamemode checks + if (next_weapon) + { + newweapon = G_NextWeapon(next_weapon); + next_weapon = 0; + } + else + { + newweapon = + (gamekeydown[key_weapon1] || mousebuttons [mb_weapon1]) ? wp_fist : // killough 5/2/98: reformatted + (gamekeydown[key_weapon2] || mousebuttons [mb_weapon2]) ? wp_pistol : + (gamekeydown[key_weapon3] || mousebuttons [mb_weapon3]) ? wp_shotgun : + (gamekeydown[key_weapon4] || mousebuttons [mb_weapon4]) ? wp_chaingun : + (gamekeydown[key_weapon5] || mousebuttons [mb_weapon5]) ? wp_missile : + (gamekeydown[key_weapon6] || mousebuttons [mb_weapon6]) && gamemode != shareware ? wp_plasma : + (gamekeydown[key_weapon7] || mousebuttons [mb_weapon7]) && gamemode != shareware ? wp_bfg : + (gamekeydown[key_weapon8] || mousebuttons [mb_weapon8]) ? wp_chainsaw : + (!demo_compatibility && (gamekeydown[key_weapon9] || mousebuttons [mb_weapon9]) && gamemode == commercial) ? wp_supershotgun : + wp_nochange; + } + + // killough 3/22/98: For network and demo consistency with the + // new weapons preferences, we must do the weapons switches here + // instead of in p_user.c. But for old demos we must do it in + // p_user.c according to the old rules. Therefore demo_compatibility + // determines where the weapons switch is made. + + // killough 2/8/98: + // Allow user to switch to fist even if they have chainsaw. + // Switch to fist or chainsaw based on preferences. + // Switch to shotgun or SSG based on preferences. + + if (!demo_compatibility) + { + const player_t *player = &players[consoleplayer]; + + // only select chainsaw from '1' if it's owned, it's + // not already in use, and the player prefers it or + // the fist is already in use, or the player does not + // have the berserker strength. + + if (newweapon==wp_fist && player->weaponowned[wp_chainsaw] && + player->readyweapon!=wp_chainsaw && + (player->readyweapon==wp_fist || + !player->powers[pw_strength] || + P_WeaponPreferred(wp_chainsaw, wp_fist))) + newweapon = wp_chainsaw; + + // Select SSG from '3' only if it's owned and the player + // does not have a shotgun, or if the shotgun is already + // in use, or if the SSG is not already in use and the + // player prefers it. + + if (newweapon == wp_shotgun && gamemode == commercial && + player->weaponowned[wp_supershotgun] && + (!player->weaponowned[wp_shotgun] || + player->readyweapon == wp_shotgun || + (player->readyweapon != wp_supershotgun && + P_WeaponPreferred(wp_supershotgun, wp_shotgun)))) + newweapon = wp_supershotgun; + } + // killough 2/8/98, 3/22/98 -- end of weapon selection changes + //} + } + + if (newweapon != wp_nochange) + { + cmd->buttons |= BT_CHANGE; + cmd->buttons |= newweapon< 1 ) + { + dclickstate = mousebuttons[mousebforward]; + if (dclickstate) + dclicks++; + if (dclicks == 2) + { + cmd->buttons |= BT_USE; + dclicks = 0; + } + else + dclicktime = 0; + } + else + if ((dclicktime += ticdup) > 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 + if ((dclicktime2 += ticdup) > 20) + { + dclicks2 = 0; + dclickstate2 = 0; + } + + }//e6y: end if (mouse_doubleclick_as_use) + + if(!movement_mousenovert) + { + forward += mousey; + } + if (strafe) + { + side += mousex / movement_mousestrafedivisor; /* mead Don't want to strafe as fast as turns.*/ + side = (side / 2) * 2; // only even values are possible + } + else + cmd->angleturn -= mousex; /* mead now have enough dynamic range 2-10-00 */ + + if (!walkcamera.type || menuactive) //e6y + mousex = mousey = 0; +#ifdef GL_DOOM + motion_blur.curr_speed_pow2 = 0; +#endif + + if (forward > MAXPLMOVE) + forward = MAXPLMOVE; + else if (forward < -MAXPLMOVE) + forward = -MAXPLMOVE; + if (side > MAXPLMOVE) + side = MAXPLMOVE; + else if (side < -MAXPLMOVE) + side = -MAXPLMOVE; + + //e6y + if (movement_strafe50) + { + if(!strafe && cmd->angleturn) + { + if (side > sidemove_normal[speed]) + side = sidemove_normal[speed]; + else if (side < -sidemove_normal[speed]) + side = -sidemove_normal[speed]; + } + } + + if (stroller) side = 0; + + cmd->forwardmove += fudgef((signed char)forward); + cmd->sidemove += side; + + if ((demorecording && !longtics) || shorttics) + { + // Chocolate Doom Mouse Behaviour + // Don't discard mouse delta even if value is too small to + // turn the player this tic + if (mouse_carrytics) { + static signed short carry = 0; + signed short desired_angleturn = cmd->angleturn + carry; + cmd->angleturn = (desired_angleturn + 128) & 0xff00; + carry = desired_angleturn - cmd->angleturn; + } + + cmd->angleturn = (((cmd->angleturn + 128) >> 8) << 8); + } + + upmove = 0; + if (gamekeydown[key_flyup]) + upmove += flyspeed[speed]; + if (gamekeydown[key_flydown]) + upmove -= flyspeed[speed]; + + // CPhipps - special events (game new/load/save/pause) + if (special_event & BT_SPECIAL) { + cmd->buttons = special_event; + special_event = 0; + } +} + +// +// G_RestartLevel +// + +void G_RestartLevel(void) +{ + special_event = BT_SPECIAL | (BTS_RESTARTLEVEL & BT_SPECIALMASK); +} + +// +// G_DoLoadLevel +// + +static void G_DoLoadLevel (void) +{ + int i; + + // Set the sky map. + // First thing, we have a dummy sky texture name, + // a flat. The data is in the WAD only because + // we look for an actual index, instead of simply + // setting one. + + skyflatnum = R_FlatNumForName ( SKYFLATNAME ); + + if (gamemapinfo && gamemapinfo->skytexture[0]) + { + skytexture = R_TextureNumForName(gamemapinfo->skytexture); + } + // DOOM determines the sky texture to be used + // depending on the current episode, and the game version. + else if (gamemode == commercial) + // || gamemode == pack_tnt //jff 3/27/98 sorry guys pack_tnt,pack_plut + // || gamemode == pack_plut) //aren't gamemodes, this was matching retail + { + skytexture = R_TextureNumForName ("SKY3"); + if (gamemap < 12) + skytexture = R_TextureNumForName ("SKY1"); + else + if (gamemap < 21) + skytexture = R_TextureNumForName ("SKY2"); + } + else //jff 3/27/98 and lets not forget about DOOM and Ultimate DOOM huh? + switch (gameepisode) + { + case 1: + skytexture = R_TextureNumForName ("SKY1"); + break; + case 2: + skytexture = R_TextureNumForName ("SKY2"); + break; + case 3: + skytexture = R_TextureNumForName ("SKY3"); + break; + case 4: // Special Edition sky + skytexture = R_TextureNumForName ("SKY4"); + break; + }//jff 3/27/98 end sky setting fix + + // [RH] Set up details about sky rendering + R_InitSkyMap (); + +#ifdef GL_DOOM + R_SetBoxSkybox(skytexture); +#endif + + levelstarttic = gametic; // for time calculation + + if (!demo_compatibility && !mbf_features) // killough 9/29/98 + basetic = gametic; + + if (wipegamestate == GS_LEVEL) + wipegamestate = -1; // force a wipe + + gamestate = GS_LEVEL; + + for (i=0 ; idata1 == key_spy && netgame && (demoplayback || !deathmatch) && + gamestate == GS_LEVEL) + { + if (ev->type == ev_keyup) + gamekeydown[key_spy] = false; + if (ev->type == ev_keydown && !gamekeydown[key_spy]) + { + gamekeydown[key_spy] = true; + do // spy mode + if (++displayplayer >= MAXPLAYERS) + displayplayer = 0; + while (!playeringame[displayplayer] && displayplayer!=consoleplayer); + + ST_Start(); // killough 3/7/98: switch status bar views too + HU_Start(); + S_UpdateSounds(players[displayplayer].mo); + R_ActivateSectorInterpolations(); + R_SmoothPlaying_Reset(NULL); + } + return true; + } + + // any other key pops up menu if in demos + // + // killough 8/2/98: enable automap in -timedemo demos + // + // killough 9/29/98: make any key pop up menu regardless of + // which kind of demo, and allow other events during playback + + if (gameaction == ga_nothing && (demoplayback || gamestate == GS_DEMOSCREEN)) + { + // killough 9/29/98: allow user to pause demos during playback + if (ev->type == ev_keydown && ev->data1 == key_pause) + { + if (paused ^= 2) + S_PauseSound(); + else + S_ResumeSound(); + return true; + } + + // killough 10/98: + // Don't pop up menu, if paused in middle + // of demo playback, or if automap active. + // Don't suck up keys, which may be cheats + +//e6y + /* + return gamestate == GS_DEMOSCREEN && + !(paused & 2) && !(automapmode & am_active) && + ((ev->type == ev_keydown) || + (ev->type == ev_mouse && ev->data1) || + (ev->type == ev_joystick && ev->data1)) ? + M_StartControlPanel(), true : false; + */ + } + + if (gamestate == GS_FINALE && F_Responder(ev)) + return true; // finale ate the event + + // If the next/previous weapon keys are pressed, set the next_weapon + // variable to change weapons when the next ticcmd is generated. + if (gamestate == GS_LEVEL && ev->type == ev_keydown) + { + if (ev->data1 == key_prevweapon) + { + next_weapon = -1; + } + else if (ev->data1 == key_nextweapon) + { + next_weapon = 1; + } + } + + switch (ev->type) + { + case ev_keydown: + if (ev->data1 == key_pause) // phares + { + special_event = BT_SPECIAL | (BTS_PAUSE & BT_SPECIALMASK); + return true; + } + if (ev->data1 data1] = true; + return true; // eat key down events + + case ev_keyup: + if (ev->data1 data1] = false; + return false; // always let key up events filter down + + case ev_mouse: + SetMouseButtons(ev->data1); + return true; // eat events + + case ev_mousemotion: + /* + * bmead@surfree.com + * Modified by Barry Mead after adding vastly more resolution + * to the Mouse Sensitivity Slider in the options menu 1-9-2000 + * Removed the mouseSensitivity "*4" to allow more low end + * sensitivity resolution especially for lsdoom users. + */ + //e6y mousex += (ev->data2*(mouseSensitivity_horiz))/10; /* killough */ + //e6y mousey += (ev->data3*(mouseSensitivity_vert))/10; /*Mead rm *4 */ + + //e6y + mousex += (AccelerateMouse(ev->data2)*(mouseSensitivity_horiz))/10; /* killough */ + if(GetMouseLook()) + if (movement_mouseinvert) + mlooky += (AccelerateMouse(ev->data3)*(mouseSensitivity_mlook))/10; + else + mlooky -= (AccelerateMouse(ev->data3)*(mouseSensitivity_mlook))/10; + else + mousey += (AccelerateMouse(ev->data3)*(mouseSensitivity_vert))/40; + + return true; // eat events + + case ev_joystick: + joybuttons[0] = ev->data1 & 1; + joybuttons[1] = ev->data1 & 2; + joybuttons[2] = ev->data1 & 4; + joybuttons[3] = ev->data1 & 8; + joybuttons[4] = ev->data1 & 16; + joybuttons[5] = ev->data1 & 32; + joybuttons[6] = ev->data1 & 64; + joybuttons[7] = ev->data1 & 128; + joyxmove = ev->data2; + joyymove = ev->data3; + return true; // eat events + + default: + break; + } + return false; +} + +// +// G_Ticker +// Make ticcmd_ts for the players. +// + +void G_Ticker (void) +{ + int i; + static gamestate_t prevgamestate; + + // CPhipps - player colour changing + if (!demoplayback && mapcolor_plyr[consoleplayer] != mapcolor_me) { + // Changed my multiplayer colour - Inform the whole game + int net_cl = LittleLong(mapcolor_me); +#ifdef HAVE_NET + D_NetSendMisc(nm_plcolour, sizeof(net_cl), &net_cl); +#endif + G_ChangedPlayerColour(consoleplayer, mapcolor_me); + } + P_MapStart(); + // do player reborns if needed + for (i=0 ; iforwardmove > TURBOTHRESHOLD && + !(gametic&31) && ((gametic>>5)&3) == i ) + { + extern char *player_names[]; + /* cph - don't use sprintf, use doom_printf */ + doom_printf ("%s is turbo!", player_names[i]); + } + + if (netgame && !netdemo && !(gametic%ticdup) ) + { + if (gametic > BACKUPTICS + && consistancy[i][buf] != cmd->consistancy) + I_Error("G_Ticker: Consistency failure (%i should be %i)", + cmd->consistancy, consistancy[i][buf]); + if (players[i].mo) + consistancy[i][buf] = players[i].mo->x; + else + consistancy[i][buf] = 0; // killough 2/14/98 + } + } + } + + // check for special buttons + for (i=0; i>BTS_SAVESHIFT; + gameaction = ga_savegame; + break; + + // CPhipps - remote loadgame request + case BTS_LOADGAME: + savegameslot = + (players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT; + gameaction = ga_loadgame; + forced_loadgame = netgame; // Force if a netgame + command_loadgame = false; + break; + + // CPhipps - Restart the level + case BTS_RESTARTLEVEL: + if (demoplayback || (compatibility_level < lxdoom_1_compatibility)) + break; // CPhipps - Ignore in demos or old games + gameaction = ga_loadlevel; + break; + } + players[i].cmd.buttons = 0; + } + } + } + } + + // cph - if the gamestate changed, we may need to clean up the old gamestate + if (gamestate != prevgamestate) { + switch (prevgamestate) { + case GS_LEVEL: + // This causes crashes at level end - Neil Stevens + // The crash is because the sounds aren't stopped before freeing them + // the following is a possible fix + // This fix does avoid the crash wowever, with this fix in, the exit + // switch sound is cut off + // S_Stop(); + // Z_FreeTags(PU_LEVEL, PU_PURGELEVEL-1); + break; + case GS_INTERMISSION: + WI_End(); + default: + break; + } + prevgamestate = gamestate; + } + + // e6y + // do nothing if a pause has been pressed during playback + // pausing during intermission can cause desynchs without that + if ((paused & 2 || (!demoplayback && menuactive && !netgame)) && gamestate != GS_LEVEL) + return; + + // do main actions + switch (gamestate) + { + case GS_LEVEL: + P_Ticker (); + P_WalkTicker(); + mlooky = 0; + AM_Ticker(); + ST_Ticker (); + HU_Ticker (); + break; + + case GS_INTERMISSION: + WI_Ticker (); + break; + + case GS_FINALE: + F_Ticker (); + break; + + case GS_DEMOSCREEN: + D_PageTicker (); + break; + } +} + +// +// PLAYER STRUCTURE FUNCTIONS +// also see P_SpawnPlayer in P_Things +// + +// +// G_PlayerFinishLevel +// Can when a player completes a level. +// + +static void G_PlayerFinishLevel(int player) +{ + player_t *p = &players[player]; + memset(p->powers, 0, sizeof p->powers); + memset(p->cards, 0, sizeof p->cards); + p->mo = NULL; // cph - this is allocated PU_LEVEL so it's gone + p->extralight = 0; // cancel gun flashes + p->fixedcolormap = 0; // cancel ir gogles + p->damagecount = 0; // no palette changes + p->bonuscount = 0; +} + +// CPhipps - G_SetPlayerColour +// Player colours stuff +// +// G_SetPlayerColour + +#include "r_draw.h" + +void G_ChangedPlayerColour(int pn, int cl) +{ + int i; + + if (!netgame) return; + + mapcolor_plyr[pn] = cl; + + // Rebuild colour translation tables accordingly + R_InitTranslationTables(); + // Change translations on existing player mobj's + for (i=0; iflags &= ~MF_TRANSLATION; + players[i].mo->flags |= ((uint_64_t)playernumtotrans[i]) << MF_TRANSSHIFT; + } + } +} + +// +// G_PlayerReborn +// Called after a player dies +// almost everything is cleared and initialized +// + +void G_PlayerReborn (int player) +{ + player_t *p; + int i; + int frags[MAXPLAYERS]; + int killcount; + int itemcount; + int secretcount; + int resurectedkillcount; //e6y + + memcpy (frags, players[player].frags, sizeof frags); + killcount = players[player].killcount; + itemcount = players[player].itemcount; + secretcount = players[player].secretcount; + resurectedkillcount = players[player].resurectedkillcount; //e6y + + p = &players[player]; + + // killough 3/10/98,3/21/98: preserve cheats across idclev + { + int cheats = p->cheats; + memset (p, 0, sizeof(*p)); + p->cheats = cheats; + } + + memcpy(players[player].frags, frags, sizeof(players[player].frags)); + players[player].killcount = killcount; + players[player].itemcount = itemcount; + players[player].secretcount = secretcount; + players[player].resurectedkillcount = resurectedkillcount; //e6y + + p->usedown = p->attackdown = true; // don't do anything immediately + p->playerstate = PST_LIVE; + p->health = initial_health; // Ty 03/12/98 - use dehacked values + p->readyweapon = p->pendingweapon = wp_pistol; + p->weaponowned[wp_fist] = true; + p->weaponowned[wp_pistol] = true; + p->ammo[am_clip] = initial_bullets; // Ty 03/12/98 - use dehacked values + + for (i=0 ; imaxammo[i] = maxammo[i]; +} + +// +// G_CheckSpot +// Returns false if the player cannot be respawned +// at the given mapthing_t spot +// because something is occupying it +// + +static dboolean G_CheckSpot(int playernum, mapthing_t *mthing) +{ + fixed_t x,y; + subsector_t *ss; + int i; + + if (!players[playernum].mo) + { + // first spawn of level, before corpses + for (i=0 ; ix == mthing->x << FRACBITS + && players[i].mo->y == mthing->y << FRACBITS) + return false; + return true; + } + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + // killough 4/2/98: fix bug where P_CheckPosition() uses a non-solid + // corpse to detect collisions with other players in DM starts + // + // Old code: + // if (!P_CheckPosition (players[playernum].mo, x, y)) + // return false; + + players[playernum].mo->flags |= MF_SOLID; + i = P_CheckPosition(players[playernum].mo, x, y); + players[playernum].mo->flags &= ~MF_SOLID; + if (!i) + return false; + + // flush an old corpse if needed + // killough 2/8/98: make corpse queue have an adjustable limit + // killough 8/1/98: Fix bugs causing strange crashes + + if (bodyquesize > 0) + { + static int queuesize; + if (queuesize < bodyquesize) + { + bodyque = realloc(bodyque, bodyquesize*sizeof*bodyque); + memset(bodyque+queuesize, 0, + (bodyquesize-queuesize)*sizeof*bodyque); + queuesize = bodyquesize; + } + if (bodyqueslot >= bodyquesize) + P_RemoveMobj(bodyque[bodyqueslot % bodyquesize]); + bodyque[bodyqueslot++ % bodyquesize] = players[playernum].mo; + } + else + if (!bodyquesize) + P_RemoveMobj(players[playernum].mo); + + // spawn a teleport fog + ss = R_PointInSubsector (x,y); + { // Teleport fog at respawn point + fixed_t xa,ya; + int an; + mobj_t *mo; + +/* BUG: an can end up negative, because mthing->angle is (signed) short. + * We have to emulate original Doom's behaviour, deferencing past the start + * of the array, into the previous array (finetangent) */ + an = ( ANG45 * ((signed)mthing->angle/45) ) >> ANGLETOFINESHIFT; + xa = finecosine[an]; + ya = finesine[an]; + + if (compatibility_level <= finaldoom_compatibility || compatibility_level == prboom_4_compatibility) + switch (an) { + case -4096: xa = finetangent[2048]; // finecosine[-4096] + ya = finetangent[0]; // finesine[-4096] + break; + case -3072: xa = finetangent[3072]; // finecosine[-3072] + ya = finetangent[1024]; // finesine[-3072] + break; + case -2048: xa = finesine[0]; // finecosine[-2048] + ya = finetangent[2048]; // finesine[-2048] + break; + case -1024: xa = finesine[1024]; // finecosine[-1024] + ya = finetangent[3072]; // finesine[-1024] + break; + case 1024: + case 2048: + case 3072: + case 4096: + case 0: break; /* correct angles set above */ + default: I_Error("G_CheckSpot: unexpected angle %d\n",an); + } + + mo = P_SpawnMobj(x+20*xa, y+20*ya, ss->sector->floorheight, MT_TFOG); + + if (players[consoleplayer].viewz != 1) + S_StartSound(mo, sfx_telept); // don't start sound on first frame + } + + return true; +} + + +// G_DeathMatchSpawnPlayer +// Spawns a player at one of the random death match spots +// called at level load and each death +// +void G_DeathMatchSpawnPlayer (int playernum) +{ + int j, selections = deathmatch_p - deathmatchstarts; + + if (selections < MAXPLAYERS) + I_Error("G_DeathMatchSpawnPlayer: Only %i deathmatch spots, %d required", + selections, MAXPLAYERS); + + for (j=0 ; j<20 ; j++) + { + int i = P_Random(pr_dmspawn) % selections; + if (G_CheckSpot (playernum, &deathmatchstarts[i]) ) + { + deathmatchstarts[i].type = playernum+1; + P_SpawnPlayer (playernum, &deathmatchstarts[i]); + return; + } + } + + // no good spot, so the player will probably get stuck + P_SpawnPlayer (playernum, &playerstarts[playernum]); +} + +// +// G_DoReborn +// + +void G_DoReborn (int playernum) +{ + if (!netgame) + gameaction = ga_loadlevel; // reload the level from scratch + else + { // respawn at the start + int i; + + // first dissasociate the corpse + players[playernum].mo->player = NULL; + + // spawn at random spot if in death match + if (deathmatch) + { + G_DeathMatchSpawnPlayer (playernum); + return; + } + + if (G_CheckSpot (playernum, &playerstarts[playernum]) ) + { + P_SpawnPlayer (playernum, &playerstarts[playernum]); + return; + } + + // try to spawn at one of the other players spots + for (i=0 ; iendpic[0] && (strcmp(gamemapinfo->endpic, "-") != 0)) + { + if (gamemapinfo->nointermission) + { + gameaction = ga_victory; + return; + } + else + { + intermission = true; + } + } + + if (secretexit) next = gamemapinfo->nextsecret; + if (next[0] == 0) next = gamemapinfo->nextmap; + if (next[0]) + { + G_ValidateMapName(next, &wminfo.nextep, &wminfo.next); + wminfo.nextep--; + wminfo.next--; + // episode change + if (wminfo.nextep != wminfo.epsd) + { + for (i = 0; i < MAXPLAYERS; i++) + players[i].didsecret = false; + } + } + + if (next[0] || intermission) + { + wminfo.didsecret = players[consoleplayer].didsecret; + wminfo.partime = gamemapinfo->partime; + if (wminfo.partime > 0) + um_pars = true; + goto frommapinfo; // skip past the default setup. + } + } + + if (gamemode != commercial) // kilough 2/7/98 + { + // Chex Quest ends after 5 levels, rather than 8. + if (gamemission == chex) + { + if (gamemap == 5) + { + gameaction = ga_victory; + return; + } + } + else + { + switch(gamemap) + { + // cph - Remove ExM8 special case, so it gets summary screen displayed + case 9: + for (i=0 ; i= 1 && gamemap <= 34) + wminfo.partime = TICRATE*cpars[gamemap-1]; + } + else + { + if (gameepisode >= 1 && gameepisode <= 4 && gamemap >= 1 && gamemap <= 9) + wminfo.partime = TICRATE*pars[gameepisode][gamemap]; + } + +frommapinfo: + + wminfo.nextmapinfo = G_LookupMapinfo(wminfo.nextep+1, wminfo.next+1); + wminfo.maxkills = totalkills; + wminfo.maxitems = totalitems; + wminfo.maxsecret = totalsecret; + wminfo.maxfrags = 0; + wminfo.pnum = consoleplayer; + + for (i=0 ; i" when the player exits the current map + if (nodrawers && (demoplayback || timingdemo)) { + if (gamemode == commercial) + lprintf(LO_INFO, "FINISHED: MAP%02d\n", gamemap); + else + lprintf(LO_INFO, "FINISHED: E%dM%d\n", gameepisode, gamemap); + } + + e6y_G_DoCompleted();//e6y + + if (gamemode == commercial || gamemap != 8) + { + StatCopy(&wminfo); + } + + WI_Start (&wminfo); +} + +// +// G_WorldDone +// + +void G_WorldDone (void) +{ + gameaction = ga_worlddone; + + if (secretexit) + players[consoleplayer].didsecret = true; + + if (gamemapinfo) + { + if (gamemapinfo->intertextsecret && secretexit) + { + if (gamemapinfo->intertextsecret[0] != '-') // '-' means that any default intermission was cleared. + F_StartFinale(); + + return; + } + else if (gamemapinfo->intertext && !secretexit) + { + if (gamemapinfo->intertext[0] != '-') // '-' means that any default intermission was cleared. + F_StartFinale(); + + return; + } + else if (gamemapinfo->endpic[0] && gamemapinfo->endpic[0] != '-' && !secretexit) + { + // game ends without a status screen. + gameaction = ga_victory; + return; + } + // if nothing applied, use the defaults. + } + + if (gamemode == commercial && gamemission != pack_nerve) + { + switch (gamemap) + { + case 15: + case 31: + if (!secretexit) + break; + // fallthrough + case 6: + case 11: + case 20: + case 30: + F_StartFinale (); + break; + } + } + else if (gamemission == pack_nerve && singleplayer && gamemap == 8) + F_StartFinale (); + else if (gamemap == 8) + gameaction = ga_victory; // cph - after ExM8 summary screen, show victory stuff +} + +void G_DoWorldDone (void) +{ + idmusnum = -1; //jff 3/17/98 allow new level's music to be loaded + gamestate = GS_LEVEL; + gameepisode = wminfo.nextep + 1; + gamemap = wminfo.next + 1; + gamemapinfo = G_LookupMapinfo(gameepisode, gamemap); + G_DoLoadLevel(); + gameaction = ga_nothing; + AM_clearMarks(); //jff 4/12/98 clear any marks on the automap + e6y_G_DoWorldDone();//e6y +} + +// killough 2/28/98: A ridiculously large number +// of players, the most you'll ever need in a demo +// or savegame. This is used to prevent problems, in +// case more players in a game are supported later. + +#define MIN_MAXPLAYERS 32 + +extern dboolean setsizeneeded; + +//CPhipps - savename variable redundant + +/* killough 12/98: + * This function returns a signature for the current wad. + * It is used to distinguish between wads, for the purposes + * of savegame compatibility warnings, and options lookups. + */ + +static uint_64_t G_UpdateSignature(uint_64_t s, const char *name) +{ + int i, lump = W_CheckNumForName(name); + if (lump != -1 && (i = lump+10) < numlumps) + do + { + int size = W_LumpLength(i); + const byte *p = W_CacheLumpNum(i); + while (size--) + s <<= 1, s += *p++; + W_UnlockLumpNum(i); + } + while (--i > lump); + return s; +} + +static uint_64_t G_Signature(void) +{ + static uint_64_t s = 0; + static dboolean computed = false; + char name[9]; + int episode, map; + + if (!computed) { + computed = true; + if (gamemode == commercial) + for (map = haswolflevels ? 32 : 30; map; map--) + sprintf(name, "map%02d", map), s = G_UpdateSignature(s, name); + else + for (episode = gamemode==retail ? 4 : + gamemode==shareware ? 1 : 3; episode; episode--) + for (map = 9; map; map--) + sprintf(name, "E%dM%d", episode, map), s = G_UpdateSignature(s, name); + } + return s; +} + +// +// killough 5/15/98: add forced loadgames, which allow user to override checks +// + +void G_ForcedLoadGame(void) +{ + // CPhipps - net loadgames are always forced, so we only reach here + // in single player + gameaction = ga_loadgame; + forced_loadgame = true; +} + +// killough 3/16/98: add slot info +// killough 5/15/98: add command-line +void G_LoadGame(int slot, dboolean command) +{ + if (!demoplayback && !command) { + // CPhipps - handle savegame filename in G_DoLoadGame + // - Delay load so it can be communicated in net game + // - store info in special_event + special_event = BT_SPECIAL | (BTS_LOADGAME & BT_SPECIALMASK) | + ((slot << BTS_SAVESHIFT) & BTS_SAVEMASK); + forced_loadgame = netgame; // CPhipps - always force load netgames + } else { + // Do the old thing, immediate load + gameaction = ga_loadgame; + forced_loadgame = false; + savegameslot = slot; + demoplayback = false; + // Don't stay in netgame state if loading single player save + // while watching multiplayer demo + netgame = false; + } + command_loadgame = command; + R_SmoothPlaying_Reset(NULL); // e6y +} + +// killough 5/15/98: +// Consistency Error when attempting to load savegame. + +static void G_LoadGameErr(const char *msg) +{ + Z_Free(savebuffer); // Free the savegame buffer + M_ForcedLoadGame(msg); // Print message asking for 'Y' to force + if (command_loadgame) // If this was a command-line -loadgame + { + D_StartTitle(); // Start the title screen + gamestate = GS_DEMOSCREEN; // And set the game state accordingly + } +} + +// CPhipps - size of version header +#define VERSIONSIZE 16 + +const char * comp_lev_str[MAX_COMPATIBILITY_LEVEL] = +{ "Doom v1.2", "Doom v1.666", "Doom/Doom2 v1.9", "Ultimate Doom/Doom95", "Final Doom", + "early DosDoom", "TASDoom", "\"boom compatibility\"", "boom v2.01", "boom v2.02", "lxdoom v1.3.2+", + "MBF", "PrBoom 2.03beta", "PrBoom v2.1.0-2.1.1", "PrBoom v2.1.2-v2.2.6", + "PrBoom v2.3.x", "PrBoom 2.4.0", "Current PrBoom" }; + +// comp_options_by_version removed - see G_Compatibility + +static byte map_old_comp_levels[] = +{ 0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; + +static const struct { + int comp_level; + const char* ver_printf; + int version; +} version_headers[] = { + /* cph - we don't need a new version_header for prboom_3_comp/v2.1.1, since + * the file format is unchanged. */ + { prboom_3_compatibility, "PrBoom %d", 210}, + { prboom_5_compatibility, "PrBoom %d", 211}, + { prboom_6_compatibility, "PrBoom %d", 212} + //e6y + ,{ doom_12_compatibility, "PrBoom %d", 100} + ,{ doom_1666_compatibility,"PrBoom %d", 101} + ,{ doom2_19_compatibility, "PrBoom %d", 102} + ,{ ultdoom_compatibility, "PrBoom %d", 103} + ,{ finaldoom_compatibility,"PrBoom %d", 104} + ,{ dosdoom_compatibility, "PrBoom %d", 105} + ,{ tasdoom_compatibility, "PrBoom %d", 106} + ,{ boom_compatibility_compatibility, "PrBoom %d", 107} + ,{ boom_201_compatibility, "PrBoom %d", 108} + ,{ boom_202_compatibility, "PrBoom %d", 109} + ,{ lxdoom_1_compatibility, "PrBoom %d", 110} + ,{ mbf_compatibility, "PrBoom %d", 111} + ,{ prboom_2_compatibility, "PrBoom %d", 113} +}; + +static const size_t num_version_headers = sizeof(version_headers) / sizeof(version_headers[0]); + +//e6y +unsigned int GetPackageVersion(void) +{ + static unsigned int PACKAGEVERSION = 0; + + //e6y: "2.4.8.2" -> 0x02040802 + if (PACKAGEVERSION == 0) + { + int b[4], i, k = 1; + memset(b, 0, sizeof(b)); + sscanf(PACKAGE_VERSION, "%d.%d.%d.%d", &b[0], &b[1], &b[2], &b[3]); + for (i = 3; i >= 0; i--, k *= 256) + { +#ifdef RANGECHECK + if (b[i] >= 256) + I_Error("Wrong version number of package: %s", PACKAGE_VERSION); +#endif + PACKAGEVERSION += b[i] * k; + } + } + return PACKAGEVERSION; +} + +// [FG] support named complevels on the command line, e.g. "-complevel boom", +// names based on the args to Chocolate Doom's "-gameversion" parameter +// and IWAD file names + +int G_GetNamedComplevel (const char *arg) +{ + int i; + + const struct { + int level; + const char *const name; + } named_complevel[] = { + {2, "1.9"}, + {2, "doom2"}, + {3, "ultimate"}, +// {3, "doom"}, // deemed too ambigious + {3, "udoom"}, + {4, "final"}, + {4, "tnt"}, + {4, "plutonia"}, + {9, "boom"}, + {11, "mbf"}, + }; + + // choose the complevel based on the IWAD + if (!strcasecmp(arg, "vanilla")) + { + if (gamemode == retail || gamemission == chex) + return 3; + else if (gamemode == commercial && (gamemission == pack_plut || gamemission == pack_tnt)) + return 4; + else + return 2; + } + + for (i = 0; i < sizeof(named_complevel)/sizeof(*named_complevel); i++) + { + if (!strcasecmp(arg, named_complevel[i].name)) + { + return named_complevel[i].level; + } + } + + return atoi(arg); +} + +//========================================================================== +// +// RecalculateDrawnSubsectors +// +// In case the subsector data is unusable this function tries to reconstruct +// if from the linedefs' ML_MAPPED info. +// +//========================================================================== + +void RecalculateDrawnSubsectors(void) +{ +#ifdef GL_DOOM + int i, j; + + for (i = 0; i < numsubsectors; i++) + { + subsector_t *sub = &subsectors[i]; + seg_t *seg = &segs[sub->firstline]; + for (j = 0; j < sub->numlines; j++, seg++) + { + if (seg->linedef && seg->linedef->flags & ML_MAPPED) + { + map_subsectors[i] = 1; + } + } + } + + gld_ResetTexturedAutomap(); +#endif +} + +void G_DoLoadGame(void) +{ + int length, i; + // CPhipps - do savegame filename stuff here + char *name; // killough 3/22/98 + int savegame_compatibility = -1; + //e6y: numeric version number of package should be zero before initializing from savegame + unsigned int packageversion = 0; + char maplump[8]; + int time, ttime; + + length = G_SaveGameName(NULL, 0, savegameslot, demoplayback); + name = malloc(length+1); + G_SaveGameName(name, length+1, savegameslot, demoplayback); + + // [crispy] loaded game must always be single player. + // Needed for ability to use a further game loading, as well as + // cheat codes and other single player only specifics. + if (!command_loadgame) + { + netdemo = false; + netgame = false; + deathmatch = false; + } + + gameaction = ga_nothing; + + length = M_ReadFile(name, &savebuffer); + if (length<=0) + I_Error("Couldn't read file %s: %s", name, "(Unknown Error)"); + free(name); + save_p = savebuffer + SAVESTRINGSIZE; + + // CPhipps - read the description field, compare with supported ones + for (i=0; (size_t)i= prboom_4_compatibility) ? *save_p : savegame_compatibility; + if (savegame_compatibility < prboom_6_compatibility) + compatibility_level = map_old_comp_levels[compatibility_level]; + save_p++; + + gameskill = *save_p++; + gameepisode = *save_p++; + gamemap = *save_p++; + gamemapinfo = G_LookupMapinfo(gameepisode, gamemap); + + for (i=0 ; iwadfile->name, gameskill + 1, + time/3600, (time%3600)/60, time%60, ttime/3600, (ttime%3600)/60, ttime%60); + + // done + Z_Free (savebuffer); + + if (setsizeneeded) + R_ExecuteSetViewSize (); + + // draw the pattern into the back screen + R_FillBackScreen (); + + /* killough 12/98: support -recordfrom and -loadgame -playdemo */ + if (!command_loadgame) + singledemo = false; /* Clear singledemo flag if loading from menu */ + else + if (singledemo) { + gameaction = ga_loadgame; /* Mark that we're loading a game before demo */ + G_DoPlayDemo(); /* This will detect it and won't reinit level */ + } else /* Command line + record means it's a recordfrom */ + if (demorecording) + G_BeginRecording(); +} + +// +// G_SaveGame +// Called by the menu task. +// Description is a 24 byte text string +// + +void G_SaveGame(int slot, char *description) +{ + strcpy(savedescription, description); + if (demoplayback) { + /* cph - We're doing a user-initiated save game while a demo is + * running so, go outside normal mechanisms + */ + savegameslot = slot; + G_DoSaveGame(true); + } + // CPhipps - store info in special_event + special_event = BT_SPECIAL | (BTS_SAVEGAME & BT_SPECIALMASK) | + ((slot << BTS_SAVESHIFT) & BTS_SAVEMASK); +#ifdef HAVE_NET + D_NetSendMisc(nm_savegamename, strlen(savedescription)+1, savedescription); +#endif +} + +// Check for overrun and realloc if necessary -- Lee Killough 1/22/98 +void (CheckSaveGame)(size_t size, const char* file, int line) +{ + size_t pos = save_p - savebuffer; + +#ifdef RANGECHECK + /* cph 2006/08/07 - after-the-fact sanity checking of CheckSaveGame calls */ + static size_t prev_check; + static const char* prevf; + static int prevl; + + if (pos > prev_check) + I_Error("CheckSaveGame at %s:%d called for insufficient buffer (%u < %u)", prevf, prevl, prev_check, pos); + prev_check = size + pos; + prevf = file; + prevl = line; +#endif + + size += 1024; // breathing room + if (pos+size > savegamesize) + save_p = (savebuffer = realloc(savebuffer, + savegamesize += (size+1023) & ~1023)) + pos; +} + +/* killough 3/22/98: form savegame name in one location + * (previously code was scattered around in multiple places) + * cph - Avoid possible buffer overflow problems by passing + * size to this function and using snprintf */ + +int G_SaveGameName(char *name, size_t size, int slot, dboolean demoplayback) +{ + const char* sgn = demoplayback ? "demosav" : savegamename; + return doom_snprintf (name, size, "%s/%s%d.dsg", basesavegame, sgn, slot); +} + +static void G_DoSaveGame (dboolean menu) +{ + char *name; + char name2[VERSIONSIZE]; + char *description; + int length, i; + //e6y: numeric version number of package + unsigned int packageversion = GetPackageVersion(); + char maplump[8]; + int time, ttime; + + gameaction = ga_nothing; // cph - cancel savegame at top of this function, + // in case later problems cause a premature exit + + length = G_SaveGameName(NULL, 0, savegameslot, demoplayback && !menu); + name = malloc(length+1); + G_SaveGameName(name, length+1, savegameslot, demoplayback && !menu); + + description = savedescription; + + save_p = savebuffer = malloc(savegamesize); + + CheckSaveGame(SAVESTRINGSIZE+VERSIONSIZE+sizeof(uint_64_t)); + memcpy (save_p, description, SAVESTRINGSIZE); + save_p += SAVESTRINGSIZE; + memset (name2,0,sizeof(name2)); + + // CPhipps - scan for the version header + for (i=0; (size_t)iindex out of P_ArchiveThinkers so the + // indices can be used by P_ArchiveWorld when the sectors are saved. + // This is so we can save the index of the mobj_t of the thinker that + // caused a sound, referenced by sector_t->soundtarget. + P_ThinkerToIndex(); + + P_ArchiveWorld(); + Z_CheckHeap(); + P_ArchiveThinkers(); + + // phares 9/13/98: Move index->mobj_t out of P_ArchiveThinkers, simply + // for symmetry with the P_ThinkerToIndex call above. + + P_IndexToThinker(); + + Z_CheckHeap(); + P_ArchiveSpecials(); + P_ArchiveRNG(); // killough 1/18/98: save RNG information + Z_CheckHeap(); + P_ArchiveMap(); // killough 1/22/98: save automap information + + *save_p++ = 0xe6; // consistancy marker + + Z_CheckHeap(); + doom_printf( "%s", M_WriteFile(name, savebuffer, save_p - savebuffer) + ? s_GGSAVED /* Ty - externalised */ + : "Game save failed!"); // CPhipps - not externalised + + /* Print some information about the save game */ + if (gamemode == commercial) + sprintf(maplump, "MAP%02d", gamemap); + else + sprintf(maplump, "E%dM%d", gameepisode, gamemap); + time = leveltime / TICRATE; + ttime = (totalleveltimes + leveltime) / TICRATE; + + lprintf(LO_INFO, "G_DoSaveGame: [%d] %s (%s), Skill %d, Level Time %02d:%02d:%02d, Total Time %02d:%02d:%02d\n", + savegameslot + 1, maplump, W_GetLumpInfoByNum(W_GetNumForName(maplump))->wadfile->name, gameskill + 1, + time/3600, (time%3600)/60, time%60, ttime/3600, (ttime%3600)/60, ttime%60); + + free(savebuffer); // killough + savebuffer = save_p = NULL; + + savedescription[0] = 0; + free(name); +} + +static skill_t d_skill; +static int d_episode; +static int d_map; + +static char *G_NewDemoName(const char *name) +{ + size_t demoname_size; + char *demoname; + FILE *fp = NULL; + static unsigned int j = 0; + + demoname_size = strlen(name) + 11; // + 11 for "-00000.lmp\0" + demoname = malloc(demoname_size); + snprintf(demoname, demoname_size, "%s.lmp", name); + + // prevent overriding demos by adding a file name suffix + for ( ; j <= 99999 && (fp = M_fopen(demoname, "rb")) != NULL; j++) + { + snprintf(demoname, demoname_size, "%s-%05d.lmp", name, j); + fclose(fp); + } + + return demoname; +} + +static const char *orig_demoname = NULL; + +void G_DeferedInitNew(skill_t skill, int episode, int map) +{ + d_skill = skill; + d_episode = episode; + d_map = map; + gameaction = ga_newgame; + + // if a new game is started during demo recording, start a new demo + if (demorecording && orig_demoname) + { + extern int ddt_cheating; + char *demoname; + + ddt_cheating = 0; + G_CheckDemoStatus(); + + demoname = G_NewDemoName(orig_demoname); + G_RecordDemo(demoname); + free(demoname); + + basetic = gametic; + } +} + +/* cph - + * G_Compatibility + * + * Initialises the comp[] array based on the compatibility_level + * For reference, MBF did: + * for (i=0; i < COMP_TOTAL; i++) + * comp[i] = compatibility; + * + * Instead, we have a lookup table showing at what version a fix was + * introduced, and made optional (replaces comp_options_by_version) + */ + +void G_Compatibility(void) +{ + static const struct { + complevel_t fix; // level at which fix/change was introduced + complevel_t opt; // level at which fix/change was made optional + } levels[] = { + // comp_telefrag - monsters used to telefrag only on MAP30, now they do it for spawners only + { mbf_compatibility, mbf_compatibility }, + // comp_dropoff - MBF encourages things to drop off of overhangs + { mbf_compatibility, mbf_compatibility }, + // comp_vile - original Doom archville bugs like ghosts + { boom_compatibility, mbf_compatibility }, + // comp_pain - original Doom limits Pain Elementals from spawning too many skulls + { boom_compatibility, mbf_compatibility }, + // comp_skull - original Doom let skulls be spit through walls by Pain Elementals + { boom_compatibility, mbf_compatibility }, + // comp_blazing - original Doom duplicated blazing door sound + { boom_compatibility, mbf_compatibility }, + // e6y: "Tagged doors don't trigger special lighting" handled wrong + // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 + // comp_doorlight - MBF made door lighting changes more gradual + { boom_compatibility, mbf_compatibility }, + // comp_model - improvements to the game physics + { boom_compatibility, mbf_compatibility }, + // comp_god - fixes to God mode + { boom_compatibility, mbf_compatibility }, + // comp_falloff - MBF encourages things to drop off of overhangs + { mbf_compatibility, mbf_compatibility }, + // comp_floors - fixes for moving floors bugs + { boom_compatibility_compatibility, mbf_compatibility }, + // comp_skymap + { mbf_compatibility, mbf_compatibility }, + // comp_pursuit - MBF AI change, limited pursuit? + { mbf_compatibility, mbf_compatibility }, + // comp_doorstuck - monsters stuck in doors fix + { boom_202_compatibility, mbf_compatibility }, + // comp_staylift - MBF AI change, monsters try to stay on lifts + { mbf_compatibility, mbf_compatibility }, + // comp_zombie - prevent dead players triggering stuff + { lxdoom_1_compatibility, mbf_compatibility }, + // comp_stairs - see p_floor.c + { boom_202_compatibility, mbf_compatibility }, + // comp_infcheat - FIXME + { mbf_compatibility, mbf_compatibility }, + // comp_zerotags - allow zero tags in wads */ + { boom_compatibility, mbf_compatibility }, + // comp_moveblock - enables keygrab and mancubi shots going thru walls + { lxdoom_1_compatibility, prboom_2_compatibility }, + // comp_respawn - objects which aren't on the map at game start respawn at (0,0) + { prboom_2_compatibility, prboom_2_compatibility }, + // comp_sound - see s_sound.c + { boom_compatibility_compatibility, prboom_3_compatibility }, + // comp_666 - emulate pre-Ultimate BossDeath behaviour + { ultdoom_compatibility, prboom_4_compatibility }, + // comp_soul - enables lost souls bouncing (see P_ZMovement) + { prboom_4_compatibility, prboom_4_compatibility }, + // comp_maskedanim - 2s mid textures don't animate + { doom_1666_compatibility, prboom_4_compatibility }, + //e6y + // comp_ouchface - Use Doom's buggy "Ouch" face code + { prboom_1_compatibility, prboom_6_compatibility }, + // comp_maxhealth - Max Health in DEH applies only to potions + { boom_compatibility_compatibility, prboom_6_compatibility }, + // comp_translucency - No predefined translucency for some things + { boom_compatibility_compatibility, prboom_6_compatibility }, + }; + unsigned int i; + + if (sizeof(levels)/sizeof(*levels) != COMP_NUM) + I_Error("G_Compatibility: consistency error"); + + for (i = 0; i < sizeof(levels)/sizeof(*levels); i++) + if (compatibility_level < levels[i].opt) + comp[i] = (compatibility_level < levels[i].fix); + + e6y_G_Compatibility();//e6y + + if (!mbf_features) { + monster_infighting = 1; + monster_backing = 0; + monster_avoid_hazards = 0; + monster_friction = 0; + help_friends = 0; + + dogs = 0; + dog_jumping = 0; + + monkeys = 0; + } +} + +/* killough 7/19/98: Marine's best friend :) */ +static int G_GetHelpers(void) +{ + int j = M_CheckParm ("-dog"); + + if (!j) + j = M_CheckParm ("-dogs"); + return j ? j+1 < myargc ? atoi(myargv[j+1]) : 1 : default_dogs; +} + +// killough 3/1/98: function to reload all the default parameter +// settings before a new game begins + +void G_ReloadDefaults(void) +{ + // killough 3/1/98: Initialize options based on config file + // (allows functions above to load different values for demos + // and savegames without messing up defaults). + + weapon_recoil = default_weapon_recoil; // weapon recoil + + player_bobbing = default_player_bobbing; // whether player bobs or not + + /* cph 2007/06/31 - for some reason, the default_* of the next 2 vars was never implemented */ + variable_friction = default_variable_friction; + allow_pushers = default_allow_pushers; + + + monsters_remember = default_monsters_remember; // remember former enemies + + monster_infighting = default_monster_infighting; // killough 7/19/98 + + dogs = netgame ? 0 : G_GetHelpers(); // killough 7/19/98 + dog_jumping = default_dog_jumping; + + distfriend = default_distfriend; // killough 8/8/98 + + monster_backing = default_monster_backing; // killough 9/8/98 + + monster_avoid_hazards = default_monster_avoid_hazards; // killough 9/9/98 + + monster_friction = default_monster_friction; // killough 10/98 + + help_friends = default_help_friends; // killough 9/9/98 + + monkeys = default_monkeys; + + // jff 1/24/98 reset play mode to command line spec'd version + // killough 3/1/98: moved to here + respawnparm = clrespawnparm; + fastparm = clfastparm; + nomonsters = clnomonsters; + + //jff 3/24/98 set startskill from defaultskill in config file, unless + // it has already been set by a -skill parameter + if (startskill==sk_none) + startskill = (skill_t)(defaultskill-1); + + demoplayback = false; + singledemo = false; // killough 9/29/98: don't stop after 1 demo + netdemo = false; + + // killough 2/21/98: + memset(playeringame+1, 0, sizeof(*playeringame)*(MAXPLAYERS-1)); + + consoleplayer = 0; + + compatibility_level = default_compatibility_level; + { + int i = M_CheckParm("-complevel"); + if (i && (1+i) < myargc) { + int l = G_GetNamedComplevel(myargv[i+1]);; + if (l >= -1) compatibility_level = l; + } + } + if (compatibility_level == -1) + compatibility_level = best_compatibility; + + if (mbf_features) + memcpy(comp, default_comp, sizeof comp); + G_Compatibility(); + + // killough 3/31/98, 4/5/98: demo sync insurance + demo_insurance = default_demo_insurance == 1; + + rngseed += I_GetRandomTimeSeed() + gametic; // CPhipps +} + +void G_DoNewGame (void) +{ + extern int solo_net; + + // e6y: allow new level's music to be loaded + idmusnum = -1; + + G_ReloadDefaults(); // killough 3/1/98 + netgame = solo_net; + deathmatch = false; + G_InitNew (d_skill, d_episode, d_map); + gameaction = ga_nothing; + + // if a new game is started during demo recording, start a new demo + if (demorecording && orig_demoname) + { + G_BeginRecording(); + } + + //jff 4/26/98 wake up the status bar in case were coming out of a DM demo + ST_Start(); + walkcamera.type=0; //e6y +} + +// killough 4/10/98: New function to fix bug which caused Doom +// lockups when idclev was used in conjunction with -fast. + +void G_SetFastParms(int fast_pending) +{ + static int fast = 0; // remembers fast state + int i; + if (fast != fast_pending) { /* only change if necessary */ + if ((fast = fast_pending)) + { + for (i=S_SARG_RUN1; i<=S_SARG_PAIN2; i++) + if (states[i].tics != 1 || demo_compatibility) // killough 4/10/98 + states[i].tics >>= 1; // don't change 1->0 since it causes cycles + mobjinfo[MT_BRUISERSHOT].speed = 20*FRACUNIT; + mobjinfo[MT_HEADSHOT].speed = 20*FRACUNIT; + mobjinfo[MT_TROOPSHOT].speed = 20*FRACUNIT; + } + else + { + 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; + } + } +} + +struct MapEntry *G_LookupMapinfo(int gameepisode, int gamemap) +{ + char lumpname[9]; + unsigned i; + if (gamemode == commercial) snprintf(lumpname, 9, "MAP%02d", gamemap); + else snprintf(lumpname, 9, "E%dM%d", gameepisode, gamemap); + for (i = 0; i < Maps.mapcount; i++) + { + if (!stricmp(lumpname, Maps.maps[i].mapname)) + { + return &Maps.maps[i]; + } + } + return NULL; +} + +struct MapEntry *G_LookupMapinfoByName(const char *lumpname) +{ + unsigned i; + for (i = 0; i < Maps.mapcount; i++) + { + if (!stricmp(lumpname, Maps.maps[i].mapname)) + { + return &Maps.maps[i]; + } + } + return NULL; +} + +int G_ValidateMapName(const char *mapname, int *pEpi, int *pMap) +{ + // Check if the given map name can be expressed as a gameepisode/gamemap pair and be reconstructed from it. + char lumpname[9], mapuname[9]; + int epi = -1, map = -1; + + if (strlen(mapname) > 8) return 0; + strncpy(mapuname, mapname, 8); + mapuname[8] = 0; + M_Strupr(mapuname); + + if (gamemode != commercial) + { + if (sscanf(mapuname, "E%dM%d", &epi, &map) != 2) return 0; + snprintf(lumpname, 9, "E%dM%d", epi, map); + } + else + { + if (sscanf(mapuname, "MAP%d", &map) != 1) return 0; + snprintf(lumpname, 9, "MAP%02d", map); + epi = 1; + } + if (pEpi) *pEpi = epi; + if (pMap) *pMap = map; + return !strcmp(mapuname, lumpname); +} + +// +// G_InitNew +// Can be called by the startup code or the menu task, +// consoleplayer, displayplayer, playeringame[] should be set. +// + +extern int EpiCustom; + +void G_InitNew(skill_t skill, int episode, int map) +{ + int i; + + // e6y + // This variable is for correct checking for upper limit of episode. + // Ultimate Doom, Final Doom and Doom95 have + // "if (episode == 0) episode = 3/4" check instead of + // "if (episode > 3/4) episode = 3/4" + dboolean fake_episode_check = + compatibility_level == ultdoom_compatibility || + compatibility_level == finaldoom_compatibility; + + if (paused) + { + paused = false; + S_ResumeSound(); + } + + if (skill > sk_nightmare) + skill = sk_nightmare; + + if (episode < 1) + episode = 1; + + // Disable all sanity checks if there are custom episode definitions. They do not make sense in this case. + if (!EpiCustom && W_CheckNumForName(MAPNAME(episode, map)) == -1) + { + + //e6y: We need to remove the fourth episode for pre-ultimate complevels. + if (compatibility_level < ultdoom_compatibility && episode > 3) + { + episode = 3; + } + + //e6y: DosDoom has only this check + if (compatibility_level == dosdoom_compatibility) + { + if (gamemode == shareware) + episode = 1; // only start episode 1 on shareware + } + else + if (gamemode == retail) + { + // e6y: Ability to play any episode with Ultimate Doom, + // Final Doom or Doom95 compatibility and -warp command line switch + // E5M1 from 2002ado.wad is an example. + // Now you can play it with "-warp 5 1 -complevel 3". + // 'Vanilla' Ultimate Doom executable also allows it. + if (fake_episode_check ? episode == 0 : episode > 4) + episode = 4; + } + else + if (gamemode == shareware) + { + if (episode > 1) + episode = 1; // only start episode 1 on shareware + } + else + // e6y: Ability to play any episode with Ultimate Doom, + // Final Doom or Doom95 compatibility and -warp command line switch + if (fake_episode_check ? episode == 0 : episode > 3) + episode = 3; + + if (map < 1) + map = 1; + if (map > 9 && gamemode != commercial) + map = 9; + } + + G_SetFastParms(fastparm || skill == sk_nightmare); // killough 4/10/98 + + M_ClearRandom(); + + respawnmonsters = skill == sk_nightmare || respawnparm; + + // force players to be initialized upon first level load + for (i=0 ; iforwardmove = (signed char)(*(*data_p)++); + cmd->sidemove = (signed char)(*(*data_p)++); + if (!longtics) + { + cmd->angleturn = ((unsigned char)(at = *(*data_p)++))<<8; + } + else + { + unsigned int lowbyte = (unsigned char)(*(*data_p)++); + cmd->angleturn = (((signed int)(*(*data_p)++))<<8) + lowbyte; + } + cmd->buttons = (unsigned char)(*(*data_p)++); + + // e6y: ability to play tasdoom demos directly + if (compatibility_level == tasdoom_compatibility) + { + signed char tmp = cmd->forwardmove; + cmd->forwardmove = cmd->sidemove; + cmd->sidemove = (signed char)at; + cmd->angleturn = ((unsigned char)cmd->buttons)<<8; + cmd->buttons = (byte)tmp; + } +} + +void G_ReadDemoTiccmd (ticcmd_t* cmd) +{ + demo_curr_tic++; + + if (*demo_p == DEMOMARKER) + { + G_CheckDemoStatus(); // end of demo data stream + } + else if (demoplayback && demo_p + bytes_per_tic > demobuffer + demolength) + { + lprintf(LO_WARN, "G_ReadDemoTiccmd: missing DEMOMARKER\n"); + G_CheckDemoStatus(); + } + else + { + G_ReadOneTick(cmd, &demo_p); + } +} + +/* Demo limits removed -- killough + * cph - record straight to file + */ +void G_WriteDemoTiccmd (ticcmd_t* cmd) +{ + char buf[5]; + char *p = buf; + + if (compatibility_level == tasdoom_compatibility) + { + *p++ = cmd->buttons; + *p++ = cmd->forwardmove; + *p++ = cmd->sidemove; + *p++ = (cmd->angleturn+128)>>8; + } else { + + *p++ = cmd->forwardmove; + *p++ = cmd->sidemove; + if (!longtics) { + *p++ = (cmd->angleturn+128)>>8; + } else { + signed short a = cmd->angleturn; + *p++ = a & 0xff; + *p++ = (a >> 8) & 0xff; + } + *p++ = cmd->buttons; + + }//e6y + + if (fwrite(buf, p-buf, 1, demofp) != 1) + I_Error("G_WriteDemoTiccmd: error writing demo"); + + /* cph - alias demo_p to it so we can read it back */ + demo_p = buf; + G_ReadDemoTiccmd (cmd); // make SURE it is exactly the same +} + +// +// G_RecordDemo +// + +void G_RecordDemo (const char* name) +{ + char *demoname; + int demoname_len; + usergame = false; + demoname_len = strlen(name)+4+1; + demoname = malloc(demoname_len); + AddDefaultExtension(strcpy(demoname, name), ".lmp"); // 1/18/98 killough + demorecording = true; + + if (demoname) + { + free(demo_filename); + demo_filename = strdup(BaseName(demoname)); + } + + // the original name chosen for the demo + if (!orig_demoname) + { + char *ext; + orig_demoname = strdup(name); + ext = strrchr(orig_demoname, '.'); + if (ext) + *ext = '\0'; + } + + /* cph - Record demos straight to file + * If file already exists, try to continue existing demo + */ + + demofp = NULL; + if (M_access(demoname, F_OK) || democontinue || + (demo_compatibility && demo_overwriteexisting)) + { + if (strlen(demoname) > 4 + && !strcasecmp(demoname+strlen(demoname)-4, ".wad")) + I_Error("G_RecordDemo: Cowardly refusing to record over " + "what appears to be a WAD. (%s)", demoname); + + demofp = M_fopen(demoname, "wb"); + } + else + { + if (demo_compatibility && !demo_overwriteexisting) + { + I_Error("G_RecordDemo: file %s already exists", name); + } + + demofp = M_fopen(demoname, "rb+"); + if (demofp) + { + int slot = -1; + const byte* pos; + byte buf[200]; + size_t len; + + //e6y: save all data which can be changed by G_ReadDemoHeader + G_SaveRestoreGameOptions(true); + + /* Read the demo header for options etc */ + len = fread(buf, 1, sizeof(buf), demofp); + pos = G_ReadDemoHeader(buf, len); + if (pos) + { + int rc; + int bytes_per_tic = longtics ? 5 : 4; + + fseek(demofp, pos - buf, SEEK_SET); + + /* Now read the demo to find the last save slot */ + do + { + byte buf[5]; + + rc = fread(buf, 1, bytes_per_tic, demofp); + if (buf[0] == DEMOMARKER || rc < bytes_per_tic-1) + { + break; + } + + if (buf[bytes_per_tic-1] & BT_SPECIAL) + { + if ((buf[bytes_per_tic-1] & BT_SPECIALMASK) == BTS_SAVEGAME) + { + slot = (buf[bytes_per_tic-1] & BTS_SAVEMASK)>>BTS_SAVESHIFT; + } + } + } + while (rc == bytes_per_tic); + + if (slot != -1) + { + /* Return to the last save position, and load the relevant savegame */ + fseek(demofp, -rc, SEEK_CUR); + G_LoadGame(slot, false); + autostart = false; + return; + } + } + + //demo cannot be continued + fclose(demofp); + if (demo_overwriteexisting) + { + //restoration of all data which could be changed by G_ReadDemoHeader + G_SaveRestoreGameOptions(false); + demofp = M_fopen(demoname, "wb"); + } + else + { + I_Error("G_RecordDemo: No save in demo, can't continue"); + } + } + } + + if (!demofp) + { + I_Error("G_RecordDemo: failed to open %s", name); + } + free(demoname); +} + +// These functions are used to read and write game-specific options in demos +// and savegames so that demo sync is preserved and savegame restoration is +// complete. Not all options (for example "compatibility"), however, should +// be loaded and saved here. It is extremely important to use the same +// positions as before for the variables, so if one becomes obsolete, the +// byte(s) should still be skipped over or padded with 0's. +// Lee Killough 3/1/98 + +byte *G_WriteOptions(byte *demo_p) +{ + byte *target = demo_p + GAME_OPTION_SIZE; + + *demo_p++ = monsters_remember; // part of monster AI + + *demo_p++ = variable_friction; // ice & mud + + *demo_p++ = weapon_recoil; // weapon recoil + + *demo_p++ = allow_pushers; // MT_PUSH Things + + *demo_p++ = 0; + + *demo_p++ = player_bobbing; // whether player bobs or not + + // killough 3/6/98: add parameters to savegame, move around some in demos + *demo_p++ = respawnparm; + *demo_p++ = fastparm; + *demo_p++ = nomonsters; + + *demo_p++ = demo_insurance; // killough 3/31/98 + + // killough 3/26/98: Added rngseed. 3/31/98: moved here + *demo_p++ = (byte)((rngseed >> 24) & 0xff); + *demo_p++ = (byte)((rngseed >> 16) & 0xff); + *demo_p++ = (byte)((rngseed >> 8) & 0xff); + *demo_p++ = (byte)( rngseed & 0xff); + + // Options new to v2.03 begin here + + *demo_p++ = monster_infighting; // killough 7/19/98 + + *demo_p++ = dogs; // killough 7/19/98 + + *demo_p++ = 0; + *demo_p++ = 0; + + *demo_p++ = (distfriend >> 8) & 0xff; // killough 8/8/98 + *demo_p++ = distfriend & 0xff; // killough 8/8/98 + + *demo_p++ = monster_backing; // killough 9/8/98 + + *demo_p++ = monster_avoid_hazards; // killough 9/9/98 + + *demo_p++ = monster_friction; // killough 10/98 + + *demo_p++ = help_friends; // killough 9/9/98 + + *demo_p++ = dog_jumping; + + *demo_p++ = monkeys; + + { // killough 10/98: a compatibility vector now + int i; + for (i=0; i < COMP_TOTAL; i++) + *demo_p++ = comp[i] != 0; + } + + *demo_p++ = (compatibility_level >= prboom_2_compatibility) && forceOldBsp; // cph 2002/07/20 + + //---------------- + // Padding at end + //---------------- + while (demo_p < target) + *demo_p++ = 0; + + if (demo_p != target) + I_Error("G_WriteOptions: GAME_OPTION_SIZE is too small"); + + return target; +} + +/* Same, but read instead of write + * cph - const byte*'s + */ + +const byte *G_ReadOptions(const byte *demo_p) +{ + const byte *target = demo_p + GAME_OPTION_SIZE; + + monsters_remember = *demo_p++; + + variable_friction = *demo_p; // ice & mud + demo_p++; + + weapon_recoil = *demo_p; // weapon recoil + demo_p++; + + allow_pushers = *demo_p; // MT_PUSH Things + demo_p++; + + demo_p++; + + player_bobbing = *demo_p; // whether player bobs or not + demo_p++; + + // killough 3/6/98: add parameters to savegame, move from demo + respawnparm = *demo_p++; + fastparm = *demo_p++; + nomonsters = *demo_p++; + + demo_insurance = *demo_p++; // killough 3/31/98 + + // killough 3/26/98: Added rngseed to demos; 3/31/98: moved here + + rngseed = *demo_p++ & 0xff; + rngseed <<= 8; + rngseed += *demo_p++ & 0xff; + rngseed <<= 8; + rngseed += *demo_p++ & 0xff; + rngseed <<= 8; + rngseed += *demo_p++ & 0xff; + + // Options new to v2.03 + if (mbf_features) + { + monster_infighting = *demo_p++; // killough 7/19/98 + + dogs = *demo_p++; // killough 7/19/98 + + demo_p += 2; + + distfriend = *demo_p++ << 8; // killough 8/8/98 + distfriend+= *demo_p++; + + monster_backing = *demo_p++; // killough 9/8/98 + + monster_avoid_hazards = *demo_p++; // killough 9/9/98 + + monster_friction = *demo_p++; // killough 10/98 + + help_friends = *demo_p++; // killough 9/9/98 + + dog_jumping = *demo_p++; // killough 10/98 + + monkeys = *demo_p++; + + { // killough 10/98: a compatibility vector now + int i; + for (i=0; i < COMP_TOTAL; i++) + comp[i] = *demo_p++; + } + + forceOldBsp = *demo_p++; // cph 2002/07/20 + } + else /* defaults for versions <= 2.02 */ + { + /* G_Compatibility will set these */ + } + + G_Compatibility(); + return target; +} + +void G_BeginRecording (void) +{ + int i; + byte *demostart, *demo_p; + int num_extensions = 0; + demostart = demo_p = malloc(1000); + longtics = 0; + + // ano - jun2019 - add the extension format if needed + if (umapinfo_loaded) + { + num_extensions++; + } + + if (num_extensions > 0) + { + // demover + *demo_p++ = 0xFF; + // signature + *demo_p++ = prdemosig[0]; // 'P' + *demo_p++ = prdemosig[1]; // 'R' + *demo_p++ = prdemosig[2]; // '+' + *demo_p++ = prdemosig[3]; // 'U' + *demo_p++ = prdemosig[4]; // 'M' + *demo_p++ = '\0'; + // extension version + *demo_p++ = 1; + // + *demo_p++ = num_extensions & 0xff; + *demo_p++ = (num_extensions >> 8) & 0xff; + + if (umapinfo_loaded) + { + int mapname_len; + // [XA] get the map name from gamemapinfo if the + // starting map has a UMAPINFO definition. if not, + // fall back to the usual MAPxx/ExMy default. + char mapname[9]; + if (gamemapinfo) + { + strncpy(mapname, gamemapinfo->mapname, 8); + } + else if(gamemode == commercial) + { + snprintf(mapname, 9, "MAP%02d", gamemap); + } + else + { + snprintf(mapname, 9, "E%dM%d", gameepisode, gamemap); + } + + mapname_len = strnlen(gamemapinfo ? gamemapinfo->mapname : mapname, 9); + + // ano - note that the format has each length by each string + // as opposed to a table of lengths + *demo_p++ = 0x08; + *demo_p++ = 'U'; + *demo_p++ = 'M'; + *demo_p++ = 'A'; + *demo_p++ = 'P'; + *demo_p++ = 'I'; + *demo_p++ = 'N'; + *demo_p++ = 'F'; + *demo_p++ = 'O'; + + // ano - to properly extend this to support other extension strings + // we wouldn't just plop this here, but right now we only support the 1 + // in the future, we should assume that chunks in the header should + // follow the order of their appearance in the extensions table. + if (mapname_len > 8) + { + I_Error("Unable to save map lump name %s, too large.", mapname); + } + + for (i = 0; i < mapname_len; i++) + { + // FIXME - the toupper is a hacky workaround for the case insensitivity + // in the current UMAPINFO reader. lump names should probably not be + // lowercase ever (?) + *demo_p++ = toupper(mapname[i]); + } + + // lets pad out any spare chars if the length was too short + for (; i < 8; i++) + { + *demo_p++ = 0; + } + } + } + // ano - done with the extension format! + + /* cph - 3 demo record formats supported: MBF+, BOOM, and Doom v1.9 */ + if (mbf_features) { + { /* Write version code into demo */ + unsigned char v = 0; + switch(compatibility_level) { + case mbf_compatibility: v = 203; break; // e6y: Bug in MBF compatibility mode fixed + case prboom_2_compatibility: v = 210; break; + case prboom_3_compatibility: v = 211; break; + case prboom_4_compatibility: v = 212; break; + case prboom_5_compatibility: v = 213; break; + case prboom_6_compatibility: + v = 214; + longtics = 1; + break; + default: I_Error("G_BeginRecording: PrBoom compatibility level unrecognised?"); + } + *demo_p++ = v; + } + + // signature + *demo_p++ = 0x1d; + *demo_p++ = 'M'; + *demo_p++ = 'B'; + *demo_p++ = 'F'; + *demo_p++ = 0xe6; + *demo_p++ = '\0'; + + /* killough 2/22/98: save compatibility flag in new demos + * cph - FIXME? MBF demos will always be not in compat. mode */ + *demo_p++ = 0; + + *demo_p++ = gameskill; + *demo_p++ = gameepisode; + *demo_p++ = gamemap; + *demo_p++ = deathmatch; + *demo_p++ = consoleplayer; + + demo_p = G_WriteOptions(demo_p); // killough 3/1/98: Save game options + + for (i=0 ; i= boom_compatibility_compatibility) { //e6y + } else if (compatibility_level > boom_compatibility_compatibility) { + byte v = 0, c = 0; /* Nominally, version and compatibility bits */ + switch (compatibility_level) { + case boom_compatibility_compatibility: v = 202, c = 1; break; + case boom_201_compatibility: v = 201; c = 0; break; + case boom_202_compatibility: v = 202, c = 0; break; + default: I_Error("G_BeginRecording: Boom compatibility level unrecognised?"); + } + *demo_p++ = v; + + // signature + *demo_p++ = 0x1d; + *demo_p++ = 'B'; + *demo_p++ = 'o'; + *demo_p++ = 'o'; + *demo_p++ = 'm'; + *demo_p++ = 0xe6; + + /* CPhipps - save compatibility level in demos */ + *demo_p++ = c; + + *demo_p++ = gameskill; + *demo_p++ = gameepisode; + *demo_p++ = gamemap; + *demo_p++ = deathmatch; + *demo_p++ = consoleplayer; + + demo_p = G_WriteOptions(demo_p); // killough 3/1/98: Save game options + + for (i=0 ; i=0) + return lev; + } + } + if (ver == 110) return tasdoom_compatibility; + if (ver < 107) return doom_1666_compatibility; + if (gamemode == retail) return ultdoom_compatibility; + if (gamemission == pack_tnt || gamemission == pack_plut) return finaldoom_compatibility; + return doom2_19_compatibility; +} + +//e6y: Check for overrun +static dboolean CheckForOverrun(const byte *start_p, const byte *current_p, size_t maxsize, size_t size, dboolean failonerror) +{ + size_t pos = current_p - start_p; + if (pos + size > maxsize) + { + if (failonerror) + I_Error("G_ReadDemoHeader: wrong demo header\n"); + else + return true; + } + return false; +} + +// e6y +// save/restore all data which could be changed by G_ReadDemoHeader +void G_SaveRestoreGameOptions(int save) +{ + typedef struct gameoption_s + { + int type; + int value_int; + int *value_p; + } gameoption_t; + + static gameoption_t gameoptions[] = + { + {1, 0, &demover}, + {1, 0, (int*)&compatibility_level}, + {1, 0, &basetic}, + {3, 0, (int*)&rngseed}, + + {1, 0, (int*)&gameskill}, + {1, 0, &gameepisode}, + {1, 0, &gamemap}, + + {2, 0, (int*)&deathmatch}, + {2, 0, (int*)&respawnparm}, + {2, 0, (int*)&fastparm}, + {2, 0, (int*)&nomonsters}, + {1, 0, &consoleplayer}, + {2, 0, (int*)&netgame}, + {2, 0, (int*)&netdemo}, + + {1, 0, &longtics}, + {1, 0, &monsters_remember}, + {1, 0, &variable_friction}, + {1, 0, &weapon_recoil}, + {1, 0, &allow_pushers}, + {1, 0, &player_bobbing}, + {1, 0, &demo_insurance}, + {1, 0, &monster_infighting}, + {1, 0, &dogs}, + {1, 0, &distfriend}, + {1, 0, &monster_backing}, + {1, 0, &monster_avoid_hazards}, + {1, 0, &monster_friction}, + {1, 0, &help_friends}, + {1, 0, &dog_jumping}, + {1, 0, &monkeys}, + + {2, 0, (int*)&forceOldBsp}, + {-1, -1, NULL} + }; + + static dboolean was_saved_once = false; + + static dboolean playeringame_o[MAXPLAYERS]; + static dboolean playerscheats_o[MAXPLAYERS]; + static int comp_o[COMP_TOTAL]; + + int i = 0; + + if (save) + { + was_saved_once = true; + } + else + { + if (!was_saved_once) + { + I_Error("G_SaveRestoreGameOptions: Trying to restore unsaved data"); + } + } + + while (gameoptions[i].value_p) + { + switch (gameoptions[i].type) + { + case 1: //int + case 2: //dboolean + case 3: //unsigned long + if (save) + { + gameoptions[i].value_int = *gameoptions[i].value_p; + } + else + { + *gameoptions[i].value_p = gameoptions[i].value_int; + } + break; + default: // unrecognised type + I_Error("G_SaveRestoreGameOptions: Unrecognised type of option"); + break; + } + + i++; + } + + for (i = 0 ; i < MAXPLAYERS; i++) + { + if (save) + { + playeringame_o[i] = playeringame[i]; + playerscheats_o[i] = players[i].cheats; + } + else + { + playeringame[i] = playeringame_o[i]; + players[i].cheats = playerscheats_o[i]; + } + } + + for (i = 0; i < COMP_TOTAL; i++) + { + if (save) + { + comp_o[i] = comp[i]; + } + else + { + comp[i] = comp_o[i]; + } + } + if (!save) G_LookupMapinfo(gameepisode, gamemap); +} + +const byte* G_ReadDemoHeader(const byte *demo_p, size_t size) +{ + return G_ReadDemoHeaderEx(demo_p, size, 0); +} + +const byte* G_ReadDemoHeaderEx(const byte *demo_p, size_t size, unsigned int params) +{ + skill_t skill; + int i, episode = 1, map = 0, extension_version; + + int using_umapinfo = 0; + + // e6y + // The local variable should be used instead of demobuffer, + // because demobuffer can be uninitialized + const byte *header_p = demo_p; + + dboolean failonerror = (params&RDH_SAFE); + + basetic = gametic; // killough 9/29/98 + + // killough 2/22/98, 2/28/98: autodetect old demos and act accordingly. + // Old demos turn on demo_compatibility => compatibility; new demos load + // compatibility flag, and other flags as well, as a part of the demo. + + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 1, failonerror)) + return NULL; + + demover = *demo_p++; + longtics = 0; + + // ano - jun2019 - special extensions. originally for UMAPINFO but + // designed to be extensible otherwise using the list of strings. + // note: i consulted the eternity engine implementation of this function + extension_version = -1; + if (demover == 255) + { + int num_extensions; + // ano - jun2019 + // so the format is + // demover byte == 255 + // "PR+UM" signature (w/ ending null terminator) + // extension_version byte. for now this should always be "1" + // 2 bytes for num_extensions (little-endian) + + // num_extensions * + // 1 byte string length + // and length chars (up to 65535 obviously) + // note that the format has each length by each string + // as opposed to a table of lengths + + // an example extensions string is "UMAPINFO". + // In no realistic scenario should num_extensions + // ever reach past 65535. + + // so that's a total of 1+6+1+2 + (n * m) bytes + ?? for extensions + // or 10 + some ?? bytes + some more ?? + + // then finally the "real" demover byte is present here + + if (CheckForOverrun(header_p, demo_p, size, 10, failonerror)) + return NULL; + + // we check for the PR+UM signature as mentioned. + // Eternity Engine also uses 255 demover, with other signatures. + if (strncmp((const char *)demo_p, prdemosig, 5) != 0) + { + I_Error("G_ReadDemoHeader: Extended demo format 255 found, but \"PR+UM\" string not found."); + } + + demo_p += 6; + extension_version = *demo_p++; + + if (extension_version != 1) + { + I_Error("G_ReadDemoHeader: Extended demo format version %d unrecognized.", extension_version); + } + + num_extensions = *demo_p++; + num_extensions |= ((unsigned int)(*demo_p++)) << 8; + + if (CheckForOverrun(header_p, demo_p, size, num_extensions, failonerror)) + return NULL; + + for (i = 0; i < num_extensions; i++) + { + int r_len = *demo_p++; + + if (CheckForOverrun(header_p, demo_p, size, r_len, failonerror)) + return NULL; + + // ano - jun2019 - when more potential extension strings get added, + // this section can become more complex + if (r_len == 8 && strncmp((const char *)demo_p, "UMAPINFO", 8) == 0) + { + if (!umapinfo_loaded && !(params & RDH_SKIP_HEADER)) + { + I_Error("G_ReadDemoHeader: Playing demo with UMAPINFO extension without UMAPINFO loaded."); + } + using_umapinfo = 1; + } + else + { + // ano - TODO better error handling here? + I_Error("G_ReadDemoHeader: Extended demo format extension unrecognized."); + } + + demo_p += r_len; + } + + // ano - jun2019 - load lump name if we're using umapinfo + // this is a bit hacky to just read in episode / map number from string + // currently PR+ doesn't support arbitrary mapnames, but one day it may, + // so this is for forward compat + if(using_umapinfo) + { + const byte *string_end = demo_p + 8; + + if (CheckForOverrun(header_p, demo_p, size, 8, failonerror)) + return NULL; + + if (strncmp((const char *)demo_p, "MAP", 3) == 0) + { + // MAPx form + episode = 1; + demo_p += 3; + map = 0; + + // we're doing hellworld number parsing and i'm sorry + // CAPTAIN, WE'RE PICKING UP SOME CODE DUPLICATION IN SECTOR 47 + for (i = 0; demo_p < string_end; i++) + { + char cur = *demo_p++; + + if (cur < '0' || cur > '9') + { + if (cur != 0) + { + I_Error("G_ReadDemoHeader: Unable to determine map for UMAPINFO demo."); + } + break; + } + + map = (map * 10) + (cur - '0'); + } + } + else if(*demo_p++ == 'E') + { + // EyMx form + episode = 0; + map = 0; + + // read in episode # + for (i = 0; demo_p < string_end; i++) + { + char cur = *demo_p++; + + if (cur < '0' || cur > '9') + { + if (cur == 0 || cur == 'M') + { + break; + } + + I_Error("G_ReadDemoHeader: Unable to determine map for UMAPINFO demo."); + } + + episode = (episode * 10) + (cur - '0'); + } + + // read in map # + for (i = 0; demo_p < string_end; i++) + { + char cur = *demo_p++; + + if (cur < '0' || cur > '9') + { + if (cur == 0) + { + break; + } + + I_Error("G_ReadDemoHeader: Unable to determine map for UMAPINFO demo."); + } + + map = (map * 10) + (cur - '0'); + } + } + else + { + I_Error("G_ReadDemoHeader: Unable to determine map for UMAPINFO demo."); + } + + demo_p = string_end; + } + + // ano - jun2019 - this is to support other demovers effectively? + // while still having the extended features + header_p = demo_p; + demover = *demo_p++; + + } + // ano - okay we are done with most of the 255 extension code past this point + // demover has hopefully been set to the new value + // the only stuff related to it will be behind extension_version checks past this point + + // e6y + // Handling of unrecognized demo formats + // Versions up to 1.2 use a 7-byte header - first byte is a skill level. + // Versions after 1.2 use a 13-byte header - first byte is a demoversion. + // BOOM's demoversion starts from 200 + if (!((demover >= 0 && demover <= 4) || + (demover >= 104 && demover <= 111) || + (demover >= 200 && demover <= 214))) + { + I_Error("G_ReadDemoHeader: Unknown demo format %d.", demover); + } + + if (demover < 200) // Autodetect old demos + { + if (demover >= 111) longtics = 1; + + // killough 3/2/98: force these variables to be 0 in demo_compatibility + + variable_friction = 0; + + weapon_recoil = 0; + + allow_pushers = 0; + + monster_infighting = 1; // killough 7/19/98 + + dogs = 0; // killough 7/19/98 + dog_jumping = 0; // killough 10/98 + + monster_backing = 0; // killough 9/8/98 + + monster_avoid_hazards = 0; // killough 9/9/98 + + monster_friction = 0; // killough 10/98 + help_friends = 0; // killough 9/9/98 + monkeys = 0; + + // killough 3/6/98: rearrange to fix savegame bugs (moved fastparm, + // respawnparm, nomonsters flags to G_LoadOptions()/G_SaveOptions()) + + if ((skill=demover) >= 100) // For demos from versions >= 1.4 + { + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 8, failonerror)) + return NULL; + + compatibility_level = G_GetOriginalDoomCompatLevel(demover); + skill = *demo_p++; + episode = *demo_p++; + map = *demo_p++; + deathmatch = *demo_p++; + respawnparm = *demo_p++; + fastparm = *demo_p++; + nomonsters = *demo_p++; + consoleplayer = *demo_p++; + } + else + { + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 2, failonerror)) + return NULL; + + compatibility_level = doom_12_compatibility; + episode = *demo_p++; + map = *demo_p++; + deathmatch = respawnparm = fastparm = + nomonsters = consoleplayer = 0; + + // e6y + // Ability to force -nomonsters and -respawn for playback of 1.2 demos. + // Demos recorded with Doom.exe 1.2 did not contain any information + // about whether these parameters had been used. In order to play them + // back, you should add them to the command-line for playback. + // There is no more desynch on mesh.lmp @ mesh.wad + // prboom -iwad doom.wad -file mesh.wad -playdemo mesh.lmp -nomonsters + // http://www.doomworld.com/idgames/index.php?id=13976 + respawnparm = M_CheckParm("-respawn"); + fastparm = M_CheckParm("-fast"); + nomonsters = M_CheckParm("-nomonsters"); + + // e6y: detection of more unsupported demo formats + if (*(header_p + size - 1) == DEMOMARKER) + { + // file size test; + // DOOM_old and HERETIC don't use maps>9; + // 2 at 4,6 means playerclass=mage -> not DOOM_old or HERETIC; + if ((size >= 8 && (size - 8) % 4 != 0) || + (map > 9) || + (size >= 6 && (*(header_p + 4) == 2 || *(header_p + 6) == 2))) + { + I_Error("Unrecognised demo format."); + } + } + + } + G_Compatibility(); + } + else // new versions of demos + { + demo_p += 6; // skip signature; + switch (demover) { + case 200: /* BOOM */ + case 201: + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 1, failonerror)) + return NULL; + + if (!*demo_p++) + compatibility_level = boom_201_compatibility; + else + compatibility_level = boom_compatibility_compatibility; + break; + case 202: + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 1, failonerror)) + return NULL; + + if (!*demo_p++) + compatibility_level = boom_202_compatibility; + else + compatibility_level = boom_compatibility_compatibility; + break; + case 203: + /* LxDoom or MBF - determine from signature + * cph - load compatibility level */ + switch (*(header_p + 2)) { + case 'B': /* LxDoom */ + /* cph - DEMOSYNC - LxDoom demos recorded in compatibility modes support dropped */ + compatibility_level = lxdoom_1_compatibility; + break; + case 'M': + compatibility_level = mbf_compatibility; + demo_p++; + break; + } + break; + case 210: + compatibility_level = prboom_2_compatibility; + demo_p++; + break; + case 211: + compatibility_level = prboom_3_compatibility; + demo_p++; + break; + case 212: + compatibility_level = prboom_4_compatibility; + demo_p++; + break; + case 213: + compatibility_level = prboom_5_compatibility; + demo_p++; + break; + case 214: + compatibility_level = prboom_6_compatibility; + longtics = 1; + demo_p++; + break; + } + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 5, failonerror)) + return NULL; + + skill = *demo_p++; + + if (!using_umapinfo) + { + // ano - jun2019 - umapinfo loads mapname earlier + episode = *demo_p++; + map = *demo_p++; + } + else + { + *demo_p++; + *demo_p++; + } + deathmatch = *demo_p++; + consoleplayer = *demo_p++; + + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, GAME_OPTION_SIZE, failonerror)) + return NULL; + + demo_p = G_ReadOptions(demo_p); // killough 3/1/98: Read game options + + if (demover == 200) // killough 6/3/98: partially fix v2.00 demos + demo_p += 256-GAME_OPTION_SIZE; + } + + if (sizeof(comp_lev_str)/sizeof(comp_lev_str[0]) != MAX_COMPATIBILITY_LEVEL) + I_Error("G_ReadDemoHeader: compatibility level strings incomplete"); + lprintf(LO_INFO, "G_DoPlayDemo: playing demo with %s compatibility\n", + comp_lev_str[compatibility_level]); + + if (demo_compatibility || demover < 200) //e6y // only 4 players can exist in old demos + { + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 4, failonerror)) + return NULL; + + for (i=0; i<4; i++) // intentionally hard-coded 4 -- killough + playeringame[i] = *demo_p++; + for (;i < MAXPLAYERS; i++) + playeringame[i] = 0; + } + else + { + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, MAXPLAYERS, failonerror)) + return NULL; + + for (i=0 ; i < MAXPLAYERS; i++) + playeringame[i] = *demo_p++; + demo_p += MIN_MAXPLAYERS - MAXPLAYERS; + } + + if (playeringame[1]) + { + netgame = true; + netdemo = true; + } + + if (!(params&RDH_SKIP_HEADER)) + { + if (gameaction != ga_loadgame) { /* killough 12/98: support -loadgame */ + G_InitNew(skill, episode, map); + } + } + + for (i=0; i 0 && demolength > 0) + { + do + { + demo_tics_count++; + p += bytes_per_tic; + } + while ((p < demobuffer + demolength) && (*p != DEMOMARKER)); + + demo_tics_count /= demo_playerscount; + + sprintf(demo_len_st, "\x1b\x35/%d:%02d", + demo_tics_count/TICRATE/60, + (demo_tics_count%(60*TICRATE))/TICRATE); + } + } + + return demo_p; +} + +void G_DoPlayDemo(void) +{ + if (LoadDemo(defdemoname, &demobuffer, &demolength, &demolumpnum)) + { + demo_p = G_ReadDemoHeaderEx(demobuffer, demolength, RDH_SAFE); + + gameaction = ga_nothing; + usergame = false; + + demoplayback = true; + R_SmoothPlaying_Reset(NULL); // e6y + } + else + { + // e6y + // Do not exit if corresponding demo lump is not found. + // It makes sense for Plutonia and TNT IWADs, which have no DEMO4 lump, + // but DEMO4 should be in a demo cycle as real Plutonia and TNT have. + // + // Plutonia/Tnt executables exit with "W_GetNumForName: DEMO4 not found" + // message after playing of DEMO3, because DEMO4 is not present + // in the corresponding IWADs. + usergame = false; + D_StartTitle(); // Start the title screen + gamestate = GS_DEMOSCREEN; // And set the game state accordingly + } +} + +/* G_CheckDemoStatus + * + * Called after a death or level completion to allow demos to be cleaned up + * Returns true if a new demo loop action will take place + */ +dboolean G_CheckDemoStatus (void) +{ + //e6y + if (doSkip && (demo_stoponend || demo_stoponnext)) + G_SkipDemoStop(); + + P_ChecksumFinal(); + + if (demorecording) + { + demorecording = false; + fputc(DEMOMARKER, demofp); + + //e6y + G_WriteDemoFooter(demofp); + fclose(demofp); + + lprintf(LO_INFO, "G_CheckDemoStatus: Demo recorded\n"); + return false; // killough + } + + if (timingdemo) + { + int endtime = I_GetTime_RealTime (); + // killough -- added fps information and made it work for longer demos: + unsigned realtics = endtime-starttime; + + M_SaveDefaults(); + + I_Error ("Timed %u gametics in %u realtics = %-.1f frames per second", + (unsigned) gametic,realtics, + (unsigned) gametic * (double) TICRATE / realtics); + } + + if (demoplayback) + { + if (singledemo) + I_SafeExit(0); // killough + + if (demolumpnum != -1) { + // cph - unlock the demo lump + W_UnlockLumpNum(demolumpnum); + demolumpnum = -1; + } + G_ReloadDefaults(); // killough 3/1/98 + netgame = false; // killough 3/29/98 + deathmatch = false; + D_AdvanceDemo (); + return true; + } + return false; +} + +// killough 1/22/98: this is a "Doom printf" for messages. I've gotten +// tired of using players->message=... and so I've added this dprintf. +// +// killough 3/6/98: Made limit static to allow z_zone functions to call +// this function, without calling realloc(), which seems to cause problems. + +#define MAX_MESSAGE_SIZE 1024 + +// CPhipps - renamed to doom_printf to avoid name collision with glibc +void doom_printf(const char *s, ...) +{ + static char msg[MAX_MESSAGE_SIZE]; + va_list v; + va_start(v,s); + doom_vsnprintf(msg,sizeof(msg),s,v); /* print message in buffer */ + va_end(v); + players[consoleplayer].message = msg; // set new message +} + +//e6y +void P_WalkTicker() +{ + int strafe; + int speed; + int tspeed; + int turnheld; + int forward; + int side; + int angturn; + + if (!walkcamera.type || menuactive) + return; + + strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] + || joybuttons[joybstrafe]; + speed = autorun || gamekeydown[key_speed] || joybuttons[joybspeed]; // phares + + forward = side = 0; + angturn = 0; + turnheld = 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 = 0; // slow turn + else + tspeed = speed; // phares + + // 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 (joyxmove > 0) + side += sidemove[speed]; + if (joyxmove < 0) + side -= sidemove[speed]; + } + else + { + if (gamekeydown[key_right] || mousebuttons[mousebturnright]) + angturn -= angleturn[tspeed]; + if (gamekeydown[key_left] || mousebuttons[mousebturnleft]) + angturn += angleturn[tspeed]; + if (joyxmove > 0) + angturn -= angleturn[tspeed]; + if (joyxmove < 0) + angturn += angleturn[tspeed]; + } + + if (gamekeydown[key_up]) + forward += forwardmove[speed]; + if (gamekeydown[key_down]) + forward -= forwardmove[speed]; + if (joyymove < 0) + forward += forwardmove[speed]; + if (joyymove > 0) + forward -= forwardmove[speed]; + if (gamekeydown[key_straferight]) + side += sidemove[speed]; + if (gamekeydown[key_strafeleft]) + side -= sidemove[speed]; + + //mouse + if (mousebuttons[mousebforward]) + forward += forwardmove[speed]; + + forward += mousey; + if (strafe) + side += mousex / 4; /* mead Don't want to strafe as fast as turns.*/ + else + angturn -= mousex; /* mead now have enough dynamic range 2-10-00 */ + + walkcamera.angle += ((angturn / 8) << ANGLETOFINESHIFT); + if(GetMouseLook()) + { + walkcamera.pitch += ((mlooky / 8) << ANGLETOFINESHIFT); + CheckPitch((signed int *) &walkcamera.pitch); + } + + if (gamekeydown[key_fire] || mousebuttons[mousebfire] || + joybuttons[joybfire]) + { + walkcamera.x = players[0].mo->x; + walkcamera.y = players[0].mo->y; + walkcamera.angle = players[0].mo->angle; + walkcamera.pitch = players[0].mo->pitch; + } + + if (forward > MAXPLMOVE) + forward = MAXPLMOVE; + else if (forward < -MAXPLMOVE) + forward = -MAXPLMOVE; + if (side > MAXPLMOVE) + side = MAXPLMOVE; + else if (side < -MAXPLMOVE) + side = -MAXPLMOVE; + + // moving forward + walkcamera.x += FixedMul ((ORIG_FRICTION / 4) * forward, + finecosine[walkcamera.angle >> ANGLETOFINESHIFT]); + walkcamera.y += FixedMul ((ORIG_FRICTION / 4) * forward, + finesine[walkcamera.angle >> ANGLETOFINESHIFT]); + + // strafing + walkcamera.x += FixedMul ((ORIG_FRICTION / 6) * side, + finecosine[(walkcamera.angle - + ANG90) >> ANGLETOFINESHIFT]); + walkcamera.y += FixedMul ((ORIG_FRICTION / 6) * side, + finesine[(walkcamera.angle - ANG90) >> ANGLETOFINESHIFT]); + + { + subsector_t *subsec = R_PointInSubsector (walkcamera.x, walkcamera.y); + walkcamera.z = subsec->sector->floorheight + 41 * FRACUNIT; + } + + mousex = mousey = 0; +} + +void P_ResetWalkcam(void) +{ + if (walkcamera.type) + { + walkcamera.PrevX = walkcamera.x; + walkcamera.PrevY = walkcamera.y; + walkcamera.PrevZ = walkcamera.z; + walkcamera.PrevAngle = walkcamera.angle; + walkcamera.PrevPitch = walkcamera.pitch; + } +} + +void P_SyncWalkcam(dboolean sync_coords, dboolean sync_sight) +{ + if (!walkcamera.type) + return; + + if (players[displayplayer].mo) + { + if (sync_sight) + { + walkcamera.angle = players[displayplayer].mo->angle; + walkcamera.pitch = players[displayplayer].mo->pitch; + } + + if(sync_coords) + { + walkcamera.x = players[displayplayer].mo->x; + walkcamera.y = players[displayplayer].mo->y; + } + } +} + +void G_ReadDemoContinueTiccmd (ticcmd_t* cmd) +{ + if (!demo_continue_p) + return; + + if (gametic <= demo_tics_count && + demo_continue_p + bytes_per_tic <= demobuffer + demolength && + *demo_continue_p != DEMOMARKER) + { + G_ReadOneTick(cmd, &demo_continue_p); + } + + if (gametic >= demo_tics_count || + demo_continue_p > demobuffer + demolength || + gamekeydown[key_demo_jointogame] || joybuttons[joybuse]) + { + demo_continue_p = NULL; + democontinue = false; + if (demo_filename) + doom_printf("Continuing demo recording: %s", demo_filename); + // Sometimes this bit is not available + if ((demo_compatibility && !prboom_comp[PC_ALLOW_SSG_DIRECT].state) || + (cmd->buttons & BT_CHANGE) == 0) + cmd->buttons |= BT_JOIN; + } +} + +//e6y +void G_CheckDemoContinue(void) +{ + if (democontinue) + { + if (LoadDemo(defdemoname, &demobuffer, &demolength, &demolumpnum)) + { + demo_continue_p = G_ReadDemoHeaderEx(demobuffer, demolength, RDH_SAFE); + + singledemo = true; + autostart = true; + G_RecordDemo(democontinuename); + G_BeginRecording(); + usergame = true; + } + } +} diff --git a/src/g_game.h b/src/g_game.h new file mode 100644 index 0000000..eaa2289 --- /dev/null +++ b/src/g_game.h @@ -0,0 +1,249 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: Main game control interface. + *-----------------------------------------------------------------------------*/ + +#ifndef __G_GAME__ +#define __G_GAME__ + +#include "doomdef.h" +#include "d_event.h" +#include "d_ticcmd.h" + +// +// GAME +// + +#define DEMOMARKER 0x80 + +// killough 5/2/98: number of bytes reserved for saving options +#define GAME_OPTION_SIZE 64 + +dboolean G_Responder(event_t *ev); +dboolean G_CheckDemoStatus(void); +void G_DeathMatchSpawnPlayer(int playernum); +void G_InitNew(skill_t skill, int episode, int map); +void G_DeferedInitNew(skill_t skill, int episode, int map); +void G_DeferedPlayDemo(const char *demo); // CPhipps - const +void G_LoadGame(int slot, dboolean is_command); // killough 5/15/98 +void G_ForcedLoadGame(void); // killough 5/15/98: forced loadgames +void G_DoLoadGame(void); +void G_SaveGame(int slot, char *description); // Called by M_Responder. +void G_BeginRecording(void); +// CPhipps - const on these string params +void G_RecordDemo(const char *name); // Only called by startup code. +void G_ExitLevel(void); +void G_SecretExitLevel(void); +void G_WorldDone(void); +void G_EndGame(void); /* cph - make m_menu.c call a G_* function for this */ +void G_Ticker(void); +void G_ScreenShot(void); +void G_ReloadDefaults(void); // killough 3/1/98: loads game defaults +int G_SaveGameName(char *, size_t, int, dboolean); /* killough 3/22/98: sets savegame filename */ +void G_SetFastParms(int); // killough 4/10/98: sets -fast parameters +void G_DoNewGame(void); +void G_DoReborn(int playernum); +void G_DoPlayDemo(void); +void G_DoCompleted(void); +void G_ReadDemoContinueTiccmd (ticcmd_t* cmd);//e6y +void G_ReadDemoTiccmd(ticcmd_t *cmd); +void G_WriteDemoTiccmd(ticcmd_t *cmd); +void G_DoWorldDone(void); +void G_Compatibility(void); +const byte *G_ReadOptions(const byte *demo_p); /* killough 3/1/98 - cph: const byte* */ +byte *G_WriteOptions(byte *demo_p); // killough 3/1/98 +void G_PlayerReborn(int player); +void G_RestartLevel(void); // CPhipps - menu involked level restart +void G_DoVictory(void); +void G_BuildTiccmd (ticcmd_t* cmd); // CPhipps - move decl to header +void G_ChangedPlayerColour(int pn, int cl); // CPhipps - On-the-fly player colour changing +void G_MakeSpecialEvent(buttoncode_t bc, ...); /* cph - new event stuff */ +int G_ValidateMapName(const char *mapname, int *pEpi, int *pMap); + +//e6y +extern dboolean democontinue; +extern char democontinuename[]; +void G_CheckDemoContinue(void); +void G_SetSpeed(void); + +//e6y +#define RDH_SAFE 0x00000001 +#define RDH_SKIP_HEADER 0x00000002 +const byte* G_ReadDemoHeaderEx(const byte* demo_p, size_t size, unsigned int params); +const byte* G_ReadDemoHeader(const byte* demo_p, size_t size); +void G_CalculateDemoParams(const byte *demo_p); + +// killough 1/18/98: Doom-style printf; killough 4/25/98: add gcc attributes +// CPhipps - renames to doom_printf to avoid name collision with glibc +void doom_printf(const char *, ...) __attribute__((format(printf,1,2))); + +// killough 5/2/98: moved from m_misc.c: + +extern int key_right; +extern int key_left; +extern int key_up; +extern int key_down; +extern int key_mlook; +extern int key_novert; +extern int key_menu_right; // phares 3/7/98 +extern int key_menu_left; // | +extern int key_menu_up; // V +extern int key_menu_down; +extern int key_menu_backspace; // ^ +extern int key_menu_escape; // | +extern int key_menu_enter; // phares 3/7/98 +extern int key_menu_clear; +extern int key_strafeleft; +extern int key_straferight; +extern int key_flyup; +extern int key_flydown; + +extern int key_fire; +extern int key_use; +extern int key_strafe; +extern int key_speed; +extern int key_escape; // phares +extern int key_savegame; // | +extern int key_loadgame; // V +extern int key_autorun; +extern int key_reverse; +extern int key_zoomin; +extern int key_zoomout; +extern int key_chat; +extern int key_backspace; +extern int key_enter; +extern int key_help; +extern int key_soundvolume; +extern int key_hud; +extern int key_quicksave; +extern int key_endgame; +extern int key_messages; +extern int key_quickload; +extern int key_quit; +extern int key_gamma; +extern int key_spy; +extern int key_pause; +extern int key_setup; +extern int key_forward; +extern int key_leftturn; +extern int key_rightturn; +extern int key_backward; +extern int key_weapontoggle; +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_weapon9; +extern int key_nextweapon; +extern int key_prevweapon; +extern int mb_weapon1; +extern int mb_weapon2; +extern int mb_weapon3; +extern int mb_weapon4; +extern int mb_weapon5; +extern int mb_weapon6; +extern int mb_weapon7; +extern int mb_weapon8; +extern int mb_weapon9; +extern int destination_keys[MAXPLAYERS]; +extern int key_map_right; +extern int key_map_left; +extern int key_map_up; +extern int key_map_down; +extern int key_map_zoomin; +extern int key_map_zoomout; +extern int key_map; +extern int key_map_gobig; +extern int key_map_follow; +extern int key_map_mark; // ^ +extern int key_map_clear; // | +extern int key_map_grid; // phares +extern int key_map_rotate; // cph - map rotation +extern int key_map_overlay;// cph - map overlay +extern int key_map_textured; //e6y: textured automap +extern int key_screenshot; // killough 2/22/98 -- add key for screenshot +extern int autorun; // always running? // phares +extern int mousebfire; +extern int mousebstrafe; +extern int mousebforward; +extern int mousebbackward; +extern int mousebturnright; +extern int mousebturnleft; +extern int mousebuse; +extern int mousebspeed; +extern int joybfire; +extern int joybstrafe; +extern int joybstrafeleft; +extern int joybstraferight; +extern int joybuse; +extern int joybspeed; + +extern int defaultskill; //jff 3/24/98 default skill +extern dboolean haswolflevels; //jff 4/18/98 wolf levels present +extern dboolean secretexit; + +extern int bodyquesize; // killough 2/8/98: adustable corpse limit + +// killough 5/2/98: moved from d_deh.c: +// Par times (new item with BOOM) - from g_game.c +extern int pars[5][10]; // hardcoded array size +extern int cpars[]; // hardcoded array size +// CPhipps - Make savedesciption visible in wider scope +#define SAVEDESCLEN 32 +extern char savedescription[SAVEDESCLEN]; // Description to save in savegame + +/* cph - compatibility level strings */ +extern const char * comp_lev_str[]; + +// e6y +// There is a new command-line switch "-shorttics". +// This makes it possible to practice routes and tricks +// (e.g. glides, where this makes a significant difference) +// with the same mouse behaviour as when recording, +// but without having to be recording every time. +extern int shorttics; + +// automatic pistol start when advancing from one level to the next +extern int pistolstart; + +//e6y: for r_demo.c +extern int longtics; +extern int bytes_per_tic; + +extern dboolean boom_autoswitch; +extern dboolean done_autoswitch; + +#define singleplayer (!demorecording && !demoplayback && !democontinue && !netgame) +#define comperr(i) (default_comperr[i] && !demorecording && !demoplayback && !democontinue && !netgame) + +#endif diff --git a/src/g_overflow.c b/src/g_overflow.c new file mode 100644 index 0000000..5008ce9 --- /dev/null +++ b/src/g_overflow.c @@ -0,0 +1,542 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "g_overflow.h" + +#include "doomstat.h" +#include "lprintf.h" +#include "m_argv.h" +#include "m_misc.h" +#include "e6y.h" + +int overflows_enabled = true; + +overrun_param_t overflows[OVERFLOW_MAX]; +const char *overflow_cfgname[OVERFLOW_MAX] = +{ + "overrun_spechit_emulate", + "overrun_reject_emulate", + "overrun_intercept_emulate", + "overrun_playeringame_emulate", + "overrun_donut_emulate", + "overrun_missedbackside_emulate" +}; + +static void ShowOverflowWarning(overrun_list_t overflow, int fatal, const char *params, ...) +{ + overflows[overflow].shit_happens = true; + + if (overflows[overflow].warn && !overflows[overflow].promted) + { + va_list argptr; + char buffer[1024]; + + static const char *name[OVERFLOW_MAX] = { + "SPECHIT", "REJECT", "INTERCEPT", "PLYERINGAME", "DONUT", "MISSEDBACKSIDE"}; + + static const char str1[] = + "Too big or not supported %s overflow has been detected. " + "Desync or crash can occur soon " + "or during playback with the vanilla engine in case you're recording demo.%s%s"; + + static const char str2[] = + "%s overflow has been detected.%s%s"; + + static const char str3[] = + "%s overflow has been detected. " + "The option responsible for emulation of this overflow is switched off " + "hence desync or crash can occur soon " + "or during playback with the vanilla engine in case you're recording demo.%s%s"; + + overflows[overflow].promted = true; + + sprintf(buffer, + (fatal ? str1 : (EMULATE(overflow) ? str2 : str3)), + name[overflow], + "\nYou can change PrBoom behaviour for this overflow through in-game menu.", + params); + + va_start(argptr, params); + I_vWarning(buffer, argptr); + va_end(argptr); + } +} + +// e6y +// +// Intercepts Overrun emulation +// See more information on: +// doomworld.com/vb/doom-speed-demos/35214-spechits-reject-and-intercepts-overflow-lists +// +// Thanks to Simon Howard (fraggle) for refactor the intercepts +// overrun code so that it should work properly on big endian machines +// as well as little endian machines. + +// Overwrite a specific memory location with a value. +static void InterceptsMemoryOverrun(int location, int value) +{ + int i, offset; + int index; + void *addr; + + i = 0; + offset = 0; + + // Search down the array until we find the right entry + + while (intercepts_overrun[i].len != 0) + { + if (offset + intercepts_overrun[i].len > location) + { + addr = intercepts_overrun[i].addr; + + // Write the value to the memory location. + // 16-bit and 32-bit values are written differently. + + if (addr != NULL) + { + if (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 += intercepts_overrun[i].len; + ++i; + } +} + +void InterceptsOverrun(int num_intercepts, intercept_t *intercept) +{ + if (num_intercepts > MAXINTERCEPTS_ORIGINAL && demo_compatibility && PROCESS(OVERFLOW_INTERCEPT)) + { + ShowOverflowWarning(OVERFLOW_INTERCEPT, false, ""); + + if (EMULATE(OVERFLOW_INTERCEPT)) + { + int 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. + + InterceptsMemoryOverrun(location, intercept->frac); + InterceptsMemoryOverrun(location + 4, intercept->isaline); + InterceptsMemoryOverrun(location + 8, (int)(intptr_t) intercept->d.thing); + } + } +} + +// e6y +// playeringame overrun emulation +// it detects and emulates overflows on vex6d.wad\bug_wald(toke).lmp, etc. +// http://www.doom2.net/doom2/research/runningbody.zip + +int PlayeringameOverrun(const mapthing_t* mthing) +{ + if (mthing->type == 0 && PROCESS(OVERFLOW_PLYERINGAME)) + { + // playeringame[-1] == players[3].didsecret + ShowOverflowWarning(OVERFLOW_PLYERINGAME, (players + 3)->didsecret, ""); + + if (EMULATE(OVERFLOW_PLYERINGAME)) + { + return true; + } + } + return false; +} + +// +// spechit overrun emulation +// + +unsigned int spechit_baseaddr = 0; + +// e6y +// Code to emulate the behavior of Vanilla Doom when encountering an overrun +// of the spechit array. +// No more desyncs on compet-n\hr.wad\hr18*.lmp, all strain.wad\map07 demos etc. +// http://www.doomworld.com/vb/showthread.php?s=&threadid=35214 +// See more information on: +// doomworld.com/vb/doom-speed-demos/35214-spechits-reject-and-intercepts-overflow-lists +void SpechitOverrun(spechit_overrun_param_t *params) +{ + int numspechit = *(params->numspechit); + + if (demo_compatibility && numspechit > 8) + { + line_t **spechit = *(params->spechit); + + ShowOverflowWarning(OVERFLOW_SPECHIT, + numspechit > + (compatibility_level == dosdoom_compatibility || + compatibility_level == tasdoom_compatibility ? 10 : 14), + "\n\nThe list of LineID leading to overrun:\n%d, %d, %d, %d, %d, %d, %d, %d, %d.", + spechit[0]->iLineID, spechit[1]->iLineID, spechit[2]->iLineID, + spechit[3]->iLineID, spechit[4]->iLineID, spechit[5]->iLineID, + spechit[6]->iLineID, spechit[7]->iLineID, spechit[8]->iLineID); + + if (EMULATE(OVERFLOW_SPECHIT)) + { + unsigned int addr; + + if (spechit_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. + + // + // Use the specified magic value when emulating spechit overruns. + // + + p = M_CheckParm("-spechit"); + + if (p > 0) + { + //baseaddr = atoi(myargv[p+1]); + M_StrToInt(myargv[p+1], (int*)&spechit_baseaddr); + } + else + { + spechit_baseaddr = DEFAULT_SPECHIT_MAGIC; + } + } + + // Calculate address used in doom2.exe + + addr = spechit_baseaddr + (params->line - lines) * 0x3E; + + if (compatibility_level == dosdoom_compatibility || compatibility_level == tasdoom_compatibility) + { + // There are no more desyncs in the following dosdoom demos: + // flsofdth.wad\fod3uv.lmp - http://www.doomworld.com/sda/flsofdth.htm + // hr.wad\hf181430.lmp - http://www.doomworld.com/tas/hf181430.zip + // hr.wad\hr181329.lmp - http://www.doomworld.com/tas/hr181329.zip + // icarus.wad\ic09uv.lmp - http://competn.doom2.net/pub/sda/i-o/icuvlmps.zip + + switch(numspechit) + { + case 9: + *(params->tmfloorz) = addr; + break; + case 10: + *(params->tmceilingz) = addr; + break; + + default: + fprintf(stderr, "SpechitOverrun: Warning: unable to emulate" + "an overrun where numspechit=%i\n", + numspechit); + break; + } + } + else + { + switch(numspechit) + { + case 9: + case 10: + case 11: + case 12: + params->tmbbox[numspechit-9] = addr; + break; + case 13: + *(params->nofit) = addr; + break; + case 14: + *(params->crushchange) = addr; + break; + + default: + lprintf(LO_ERROR, "SpechitOverrun: Warning: unable to emulate" + " an overrun where numspechit=%i\n", + numspechit); + break; + } + } + } + } +} + +// +// reject overrun emulation +// + +// padding the reject table if it is too short +// totallines must be the number returned by P_GroupLines() +// an underflow will be padded with zeroes, or a doom.exe z_zone header +// +// e6y +// reject overrun emulation code +// It's emulated successfully if the size of overflow no more than 16 bytes. +// No more desync on teeth-32.wad\teeth-32.lmp. +// http://www.doomworld.com/vb/showthread.php?s=&threadid=35214 + +void RejectOverrun(int rejectlump, const byte **rejectmatrix, int totallines) +{ + unsigned int length, required; + byte *newreject; + unsigned char pad; + + required = (numsectors * numsectors + 7) / 8; + length = W_LumpLength(rejectlump); + + if (length < required) + { + // allocate a new block and copy the reject table into it; zero the rest + // PU_LEVEL => will be freed on level exit + newreject = Z_Malloc(required, PU_LEVEL, NULL); + *rejectmatrix = memmove(newreject, *rejectmatrix, length); + + // e6y + // PrBoom 2.2.5 and 2.2.6 padded a short REJECT with 0xff + // This command line switch is needed for all potential demos + // recorded with these versions of PrBoom on maps with too short REJECT + // I don't think there are any demos that will need it but yes that seems sensible + pad = prboom_comp[PC_REJECT_PAD_WITH_FF].state ? 0xff : 0; + + memset(newreject + length, pad, required - length); + // unlock the original lump, it is no longer needed + W_UnlockLumpNum(rejectlump); + rejectlump = -1; + + if (demo_compatibility && PROCESS(OVERFLOW_REJECT)) + { + ShowOverflowWarning(OVERFLOW_REJECT, (required - length > 16) || (length%4 != 0), ""); + + if (EMULATE(OVERFLOW_REJECT)) + { + // merged in RejectOverrunAddInt(), and the 4 calls to it, here + unsigned int rejectpad[4] = { + 0, // size, will be filled in using totallines + 0, // part of the header of a doom.exe z_zone block + 50, // DOOM_CONST_PU_LEVEL + 0x1d4a11 // DOOM_CONST_ZONEID + }; + unsigned int i, pad = 0, *src = rejectpad; + byte *dest = newreject + length; + + rejectpad[0] = ((totallines*4+3)&~3)+24; // doom.exe zone header size + + // copy at most 16 bytes from rejectpad + // emulating a 32-bit, little-endian architecture (can't memmove) + for (i = 0; i < (unsigned int)(required - length) && i < 16; i++) { // 16 hard-coded + if (!(i&3)) // get the next 4 bytes to copy when i=0,4,8,12 + pad = *src++; + *dest++ = pad & 0xff; // store lowest-significant byte + pad >>= 8; // rotate the next byte down + } + } + } + + lprintf(LO_WARN, "P_LoadReject: REJECT too short (%u<%u) - padded\n", length, required); + } +} + +// +// Read Access Violation emulation. +// + +// 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) + +#define DOS_MEM_DUMP_SIZE 10 + +unsigned char mem_dump_dos622[DOS_MEM_DUMP_SIZE] = { + 0x57, 0x92, 0x19, 0x00, 0xF4, 0x06, 0x70, 0x00, 0x16, 0x00}; +unsigned char mem_dump_win98[DOS_MEM_DUMP_SIZE] = { + 0x9E, 0x0F, 0xC9, 0x00, 0x65, 0x04, 0x70, 0x00, 0x16, 0x00}; +unsigned char mem_dump_dosbox[DOS_MEM_DUMP_SIZE] = { + 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00}; + +unsigned char *dos_mem_dump = mem_dump_dos622; + +static int GetMemoryValue(unsigned int offset, void *value, int size) +{ + static int firsttime = true; + + if (firsttime) + { + int p, i, val; + + firsttime = false; + i = 0; + + if ((p = M_CheckParm("-setmem")) && (p < myargc-1)) + { + 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 + { + while (++p != myargc && *myargv[p] != '-' && i < DOS_MEM_DUMP_SIZE) + { + M_StrToInt(myargv[p], &val); + dos_mem_dump[i++] = (unsigned char)val; + } + } + } + } + + if (value) + { + switch (size) + { + case 1: + *((unsigned char*)value) = *((unsigned char*)(&dos_mem_dump[offset])); + return true; + case 2: + *((unsigned short*)value) = *((unsigned short*)(&dos_mem_dump[offset])); + return true; + case 4: + *((unsigned int*)value) = *((unsigned int*)(&dos_mem_dump[offset])); + return true; + } + } + + return false; +} + +// +// donut overrun emulation (linedef action #9) +// + +#define DONUT_FLOORPIC_DEFAULT 0x16 +int DonutOverrun(fixed_t *pfloorheight, short *pfloorpic) +{ + if (demo_compatibility && PROCESS(OVERFLOW_DONUT)) + { + ShowOverflowWarning(OVERFLOW_DONUT, 0, ""); + + if (EMULATE(OVERFLOW_DONUT)) + { + if (pfloorheight && pfloorpic) + { + GetMemoryValue(0, pfloorheight, 4); + GetMemoryValue(8, pfloorpic, 2); + + // bounds-check floorpic + if ((*pfloorpic) <= 0 || (*pfloorpic) >= numflats) + { + *pfloorpic = MIN(numflats - 1, DONUT_FLOORPIC_DEFAULT); + } + + return true; + } + } + } + + return false; +} + + +int MissedBackSideOverrun(line_t *line) +{ + if (demo_compatibility) + { + if (line) + { + ShowOverflowWarning(OVERFLOW_MISSEDBACKSIDE, 0, + "\n\nLinedef %d has two-sided flag set, but no second sidedef", + line->iLineID); + } + else + { + ShowOverflowWarning(OVERFLOW_MISSEDBACKSIDE, 0, ""); + } + } + + return false; +} + +// +// GetSectorAtNullAddress +// +sector_t* GetSectorAtNullAddress(void) +{ + static int null_sector_is_initialized = false; + static sector_t null_sector; + + if (demo_compatibility && EMULATE(OVERFLOW_MISSEDBACKSIDE)) + { + if (!null_sector_is_initialized) + { + memset(&null_sector, 0, sizeof(null_sector)); + null_sector.flags = NULL_SECTOR; + GetMemoryValue(0, &null_sector.floorheight, 4); + GetMemoryValue(4, &null_sector.ceilingheight, 4); + null_sector_is_initialized = true; + } + + return &null_sector; + } + + return 0; +} diff --git a/src/g_overflow.h b/src/g_overflow.h new file mode 100644 index 0000000..27a61b5 --- /dev/null +++ b/src/g_overflow.h @@ -0,0 +1,139 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System interface, sound. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __G_OVERFLOW__ +#define __G_OVERFLOW__ + +#include "doomtype.h" +#include "doomdata.h" +#include "p_maputl.h" + +typedef struct overrun_param_s +{ + int warn; + int emulate; + int footer; + int footer_emulate; + int promted; + int shit_happens; +} overrun_param_t; + +typedef enum overrun_list_s +{ + OVERFLOW_SPECHIT, + OVERFLOW_REJECT, + OVERFLOW_INTERCEPT, + OVERFLOW_PLYERINGAME, + OVERFLOW_DONUT, + OVERFLOW_MISSEDBACKSIDE, + + OVERFLOW_MAX //last +} overrun_list_t; + +extern int overflows_enabled; +extern overrun_param_t overflows[]; +extern const char *overflow_cfgname[OVERFLOW_MAX]; + +#define EMULATE(overflow) (overflows_enabled && (overflows[overflow].footer ? overflows[overflow].footer_emulate : overflows[overflow].emulate)) +#define PROCESS(overflow) (overflows_enabled && (overflows[overflow].warn || EMULATE(overflow))) + +// e6y +// +// intercepts overrun emulation +// See more information on: +// doomworld.com/vb/doom-speed-demos/35214-spechits-reject-and-intercepts-overflow-lists +// +// Thanks to Simon Howard (fraggle) for refactor the intercepts +// overrun code so that it should work properly on big endian machines +// as well as little endian machines. + +#define MAXINTERCEPTS_ORIGINAL 128 + +typedef struct +{ + int len; + void *addr; + dboolean int16_array; +} intercepts_overrun_t; + +extern intercepts_overrun_t intercepts_overrun[]; +void InterceptsOverrun(int num_intercepts, intercept_t *intercept); + +// +// playeringame overrun emulation +// + +int PlayeringameOverrun(const mapthing_t* mthing); + +// +// spechit overrun emulation +// + +// Spechit overrun magic value. +#define DEFAULT_SPECHIT_MAGIC 0x01C09C98 + +typedef struct spechit_overrun_param_s +{ + line_t *line; + + line_t ***spechit; + int *numspechit; + + fixed_t *tmbbox; + fixed_t *tmfloorz; + fixed_t *tmceilingz; + + dboolean *crushchange; + dboolean *nofit; +} spechit_overrun_param_t; + +extern unsigned int spechit_baseaddr; + +void SpechitOverrun(spechit_overrun_param_t *params); + +// +// reject overrun emulation +// + +void RejectOverrun(int rejectlump, const byte **rejectmatrix, int totallines); + +// +// donut overrun emulation (linedef action 9) +// + +int DonutOverrun(fixed_t *pfloorheight, short *pfloorpic); + +int MissedBackSideOverrun(line_t *line); +sector_t* GetSectorAtNullAddress(void); + +#endif // __G_OVERFLOW__ diff --git a/src/gl_clipper.c b/src/gl_clipper.c new file mode 100644 index 0000000..ff17d9a --- /dev/null +++ b/src/gl_clipper.c @@ -0,0 +1,463 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +/* +* +** gl_clipper.cpp +** +** Handles visibility checks. +** Loosely based on the JDoom clipper. +** +**--------------------------------------------------------------------------- +** Copyright 2003 Tim Stump +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include +#include "v_video.h" +#include "gl_intern.h" +#include "r_main.h" +#include "e6y.h" + +float frustum[6][4]; + +typedef struct clipnode_s +{ + struct clipnode_s *prev, *next; + angle_t start, end; +} clipnode_t; + +clipnode_t *freelist; +clipnode_t *clipnodes; +clipnode_t *cliphead; + +static clipnode_t * gld_clipnode_GetNew(void); +static clipnode_t * gld_clipnode_NewRange(angle_t start, angle_t end); +static dboolean gld_clipper_IsRangeVisible(angle_t startAngle, angle_t endAngle); +static void gld_clipper_AddClipRange(angle_t start, angle_t end); +static void gld_clipper_RemoveRange(clipnode_t * range); +static void gld_clipnode_Free(clipnode_t *node); + +static clipnode_t * gld_clipnode_GetNew(void) +{ + if (freelist) + { + clipnode_t * p = freelist; + freelist = p->next; + return p; + } + else + { + return malloc(sizeof(clipnode_t)); + } +} + +static clipnode_t * gld_clipnode_NewRange(angle_t start, angle_t end) +{ + clipnode_t * c = gld_clipnode_GetNew(); + c->start = start; + c->end = end; + c->next = c->prev=NULL; + return c; +} + +dboolean gld_clipper_SafeCheckRange(angle_t startAngle, angle_t endAngle) +{ + if(startAngle > endAngle) + { + return (gld_clipper_IsRangeVisible(startAngle, ANGLE_MAX) || gld_clipper_IsRangeVisible(0, endAngle)); + } + + return gld_clipper_IsRangeVisible(startAngle, endAngle); +} + +static dboolean gld_clipper_IsRangeVisible(angle_t startAngle, angle_t endAngle) +{ + clipnode_t *ci; + ci = cliphead; + + if (endAngle == 0 && ci && ci->start == 0) + return false; + + while (ci != NULL && ci->start < endAngle) + { + if (startAngle >= ci->start && endAngle <= ci->end) + { + return false; + } + ci = ci->next; + } + + return true; +} + +static void gld_clipnode_Free(clipnode_t *node) +{ + node->next = freelist; + freelist = node; +} + +static void gld_clipper_RemoveRange(clipnode_t *range) +{ + if (range == cliphead) + { + cliphead = cliphead->next; + } + else + { + if (range->prev) + { + range->prev->next = range->next; + } + if (range->next) + { + range->next->prev = range->prev; + } + } + + gld_clipnode_Free(range); +} + +void gld_clipper_SafeAddClipRange(angle_t startangle, angle_t endangle) +{ + if(startangle > endangle) + { + // The range has to added in two parts. + gld_clipper_AddClipRange(startangle, ANGLE_MAX); + gld_clipper_AddClipRange(0, endangle); + } + else + { + // Add the range as usual. + gld_clipper_AddClipRange(startangle, endangle); + } +} + +angle_t gld_clipper_AngleToPseudo(angle_t ang) +{ + double vecx = cos(ang * M_PI / ANG180); + double vecy = sin(ang * M_PI / ANG180); + + double result = vecy / (fabs(vecx) + fabs(vecy)); + if (vecx < 0) + { + result = 2.f - result; + } + return (angle_t)xs_CRoundToUInt(result * (1<<30)); +} + +void gld_clipper_SafeAddClipRangeRealAngles(angle_t startangle, angle_t endangle) +{ + gld_clipper_SafeAddClipRange( + gld_clipper_AngleToPseudo(startangle), + gld_clipper_AngleToPseudo(endangle)); +} + + +static void gld_clipper_AddClipRange(angle_t start, angle_t end) +{ + clipnode_t *node, *temp, *prevNode, *node2, *delnode; + + if (cliphead) + { + //check to see if range contains any old ranges + node = cliphead; + while (node != NULL && node->start < end) + { + if (node->start >= start && node->end <= end) + { + temp = node; + node = node->next; + gld_clipper_RemoveRange(temp); + } + else + { + if (node->start <= start && node->end >= end) + { + return; + } + else + { + node = node->next; + } + } + } + + //check to see if range overlaps a range (or possibly 2) + node = cliphead; + while (node != NULL && node->start <= end) + { + if (node->end >= start) + { + // we found the first overlapping node + if (node->start > start) + { + // the new range overlaps with this node's start point + node->start = start; + } + if (node->end < end) + { + node->end = end; + } + + node2 = node->next; + while (node2 && node2->start <= node->end) + { + if (node2->end > node->end) + { + node->end = node2->end; + } + + delnode = node2; + node2 = node2->next; + gld_clipper_RemoveRange(delnode); + } + return; + } + node = node->next; + } + + //just add range + node = cliphead; + prevNode = NULL; + temp = gld_clipnode_NewRange(start, end); + while (node != NULL && node->start < end) + { + prevNode = node; + node = node->next; + } + temp->next = node; + if (node == NULL) + { + temp->prev = prevNode; + if (prevNode) + { + prevNode->next = temp; + } + if (!cliphead) + { + cliphead = temp; + } + } + else + { + if (node == cliphead) + { + cliphead->prev = temp; + cliphead = temp; + } + else + { + temp->prev = prevNode; + prevNode->next = temp; + node->prev = temp; + } + } + } + else + { + temp = gld_clipnode_NewRange(start, end); + cliphead = temp; + return; + } +} + +void gld_clipper_Clear(void) +{ + clipnode_t *node = cliphead; + clipnode_t *temp; + + while (node != NULL) + { + temp = node; + node = node->next; + gld_clipnode_Free(temp); + } + + cliphead = NULL; +} + +angle_t gld_FrustumAngle(void) +{ + double floatangle; + angle_t a1; + + float tilt = (float)fabs(((double)(int)viewpitch) / ANG1); + if (tilt > 90.0f) + { + tilt = 90.0f; + } + + // If the pitch is larger than this you can look all around at a FOV of 90 + if (D_abs((int) viewpitch) > 46 * ANG1) + return 0xffffffff; + + // ok, this is a gross hack that barely works... + // but at least it doesn't overestimate too much... + floatangle = 2.0f + (45.0f + (tilt / 1.9f)) * (float)render_fov * ratio_scale / render_multiplier / 90.0f; + a1 = (angle_t)xs_CRoundToInt(ANG1 * floatangle); + if (a1 >= ANG180) + return 0xffffffff; + return a1; +} + +// +// gld_FrustrumSetup +// + +#define CALCMATRIX(a, b, c, d, e, f, g, h)\ + (float)(modelMatrix[a] * projMatrix[b] + \ + modelMatrix[c] * projMatrix[d] + \ + modelMatrix[e] * projMatrix[f] + \ + modelMatrix[g] * projMatrix[h]) + +#define NORMALIZE_PLANE(i)\ + t = (float)sqrt(\ + frustum[i][0] * frustum[i][0] + \ + frustum[i][1] * frustum[i][1] + \ + frustum[i][2] * frustum[i][2]); \ + frustum[i][0] /= t; \ + frustum[i][1] /= t; \ + frustum[i][2] /= t; \ + frustum[i][3] /= t + +void gld_FrustrumSetup(void) +{ + float t; + float clip[16]; + + clip[0] = CALCMATRIX(0, 0, 1, 4, 2, 8, 3, 12); + clip[1] = CALCMATRIX(0, 1, 1, 5, 2, 9, 3, 13); + clip[2] = CALCMATRIX(0, 2, 1, 6, 2, 10, 3, 14); + clip[3] = CALCMATRIX(0, 3, 1, 7, 2, 11, 3, 15); + + clip[4] = CALCMATRIX(4, 0, 5, 4, 6, 8, 7, 12); + clip[5] = CALCMATRIX(4, 1, 5, 5, 6, 9, 7, 13); + clip[6] = CALCMATRIX(4, 2, 5, 6, 6, 10, 7, 14); + clip[7] = CALCMATRIX(4, 3, 5, 7, 6, 11, 7, 15); + + clip[8] = CALCMATRIX(8, 0, 9, 4, 10, 8, 11, 12); + clip[9] = CALCMATRIX(8, 1, 9, 5, 10, 9, 11, 13); + clip[10] = CALCMATRIX(8, 2, 9, 6, 10, 10, 11, 14); + clip[11] = CALCMATRIX(8, 3, 9, 7, 10, 11, 11, 15); + + clip[12] = CALCMATRIX(12, 0, 13, 4, 14, 8, 15, 12); + clip[13] = CALCMATRIX(12, 1, 13, 5, 14, 9, 15, 13); + clip[14] = CALCMATRIX(12, 2, 13, 6, 14, 10, 15, 14); + clip[15] = CALCMATRIX(12, 3, 13, 7, 14, 11, 15, 15); + + // Right plane + frustum[0][0] = clip[ 3] - clip[ 0]; + frustum[0][1] = clip[ 7] - clip[ 4]; + frustum[0][2] = clip[11] - clip[ 8]; + frustum[0][3] = clip[15] - clip[12]; + NORMALIZE_PLANE(0); + + // Left plane + frustum[1][0] = clip[ 3] + clip[ 0]; + frustum[1][1] = clip[ 7] + clip[ 4]; + frustum[1][2] = clip[11] + clip[ 8]; + frustum[1][3] = clip[15] + clip[12]; + NORMALIZE_PLANE(1); + + // Bottom plane + frustum[2][0] = clip[ 3] + clip[ 1]; + frustum[2][1] = clip[ 7] + clip[ 5]; + frustum[2][2] = clip[11] + clip[ 9]; + frustum[2][3] = clip[15] + clip[13]; + NORMALIZE_PLANE(2); + + // Top plane + frustum[3][0] = clip[ 3] - clip[ 1]; + frustum[3][1] = clip[ 7] - clip[ 5]; + frustum[3][2] = clip[11] - clip[ 9]; + frustum[3][3] = clip[15] - clip[13]; + NORMALIZE_PLANE(3); + + // Far plane + frustum[4][0] = clip[ 3] - clip[ 2]; + frustum[4][1] = clip[ 7] - clip[ 6]; + frustum[4][2] = clip[11] - clip[10]; + frustum[4][3] = clip[15] - clip[14]; + NORMALIZE_PLANE(4); + + // Near plane + frustum[5][0] = clip[ 3] + clip[ 2]; + frustum[5][1] = clip[ 7] + clip[ 6]; + frustum[5][2] = clip[11] + clip[10]; + frustum[5][3] = clip[15] + clip[14]; + NORMALIZE_PLANE(5); +} + +dboolean gld_SphereInFrustum(float x, float y, float z, float radius) +{ + int p; + + for (p = 0; p < 4; p++) + { + if (frustum[p][0] * x + + frustum[p][1] * y + + frustum[p][2] * z + + frustum[p][3] <= -radius) + { + return false; + } + } + return true; +} diff --git a/src/gl_detail.c b/src/gl_detail.c new file mode 100644 index 0000000..89305e9 --- /dev/null +++ b/src/gl_detail.c @@ -0,0 +1,875 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gl_opengl.h" + +#include "z_zone.h" +#include + +#ifdef HAVE_LIBSDL2_IMAGE +#include +#endif + +#include + +#include "v_video.h" +#include "r_main.h" +#include "gl_intern.h" +#include "w_wad.h" +#include "lprintf.h" +#include "p_spec.h" +#include "m_misc.h" +#include "sc_man.h" +#include "e6y.h" +#include "i_system.h" + +int render_usedetail; +int gl_allow_detail_textures; +int gl_detail_maxdist; +float gl_detail_maxdist_sqrt; + +detail_t *details; +int details_count; +int details_size; +int scene_has_details; +int scene_has_wall_details; +int scene_has_flat_details; + +typedef enum +{ + TAG_DETAIL_WALL, + TAG_DETAIL_FLAT, + TAG_DETAIL_MAX, +} tag_detail_e; + +static const char *DetailItem_Keywords[TAG_DETAIL_MAX + 1] = +{ + "walls", + "flats", + NULL +}; + +static GLuint last_detail_texid = -1; + +float xCamera,yCamera,zCamera; +TAnimItemParam *anim_flats = NULL; +TAnimItemParam *anim_textures = NULL; + +void gld_ShutdownDetail(void); + +void M_ChangeUseDetail(void) +{ + render_usedetail = false; + + if (V_GetMode() == VID_MODEGL) + { + render_usedetail = gl_allow_detail_textures; + gld_EnableDetail(true); + gld_EnableDetail(false); + gld_FlushTextures(); + } +} + +float distance2piece(float x0, float y0, float x1, float y1, float x2, float y2) +{ + float t, w; + float x01 = x0 - x1; + float x02 = x0 - x2; + float x21 = x2 - x1; + float y01 = y0 - y1; + float y02 = y0 - y2; + float y21 = y2 - y1; + + if((x01 * x21 + y01 * y21) * (x02 * x21 + y02 * y21) > 0.0001f) + { + t = x01 * x01 + y01 * y01; + w = x02 * x02 + y02 * y02; + if (w < t) t = w; + } + else + { + float i1 = x01 * y21 - y01 * x21; + float i2 = x21 * x21 + y21 * y21; + t = (i1 * i1) / i2; + } + + return t; +} + +int gld_IsDetailVisible(float x0, float y0, float x1, float y1, float x2, float y2) +{ + if (gl_detail_maxdist_sqrt == 0) + { + return true; + } + else + { + return (distance2piece(x0, y0, x1, y1, x2, y2) < gl_detail_maxdist_sqrt); + } +} + +void gld_InitDetail(void) +{ + gl_detail_maxdist_sqrt = (float)sqrt((float)gl_detail_maxdist); + + I_AtExit(gld_ShutdownDetail, true); + M_ChangeUseDetail(); +} + +void gld_ShutdownDetail(void) +{ + int i; + + if (details) + { + for (i = 0; i < details_count; i++) + { + glDeleteTextures(1, &details[i].texid); + } + + free(details); + details = NULL; + details_count = 0; + details_size = 0; + } +} + +void gld_DrawTriangleStripARB(GLWall *wall, gl_strip_coords_t *c1, gl_strip_coords_t *c2) +{ + glBegin(GL_TRIANGLE_STRIP); + + // lower left corner + GLEXT_glMultiTexCoord2fvARB(GL_TEXTURE0_ARB,(const GLfloat*)&c1->t[0]); + GLEXT_glMultiTexCoord2fvARB(GL_TEXTURE1_ARB,(const GLfloat*)&c2->t[0]); + glVertex3fv((const GLfloat*)&c1->v[0]); + + // split left edge of wall + //if (!wall->glseg->fracleft) + // gld_SplitLeftEdge(wall, true); + + // upper left corner + GLEXT_glMultiTexCoord2fvARB(GL_TEXTURE0_ARB,(const GLfloat*)&c1->t[1]); + GLEXT_glMultiTexCoord2fvARB(GL_TEXTURE1_ARB,(const GLfloat*)&c2->t[1]); + glVertex3fv((const GLfloat*)&c1->v[1]); + + // upper right corner + GLEXT_glMultiTexCoord2fvARB(GL_TEXTURE0_ARB,(const GLfloat*)&c1->t[2]); + GLEXT_glMultiTexCoord2fvARB(GL_TEXTURE1_ARB,(const GLfloat*)&c2->t[2]); + glVertex3fv((const GLfloat*)&c1->v[2]); + + // split right edge of wall + //if (!wall->glseg->fracright) + // gld_SplitRightEdge(wall, true); + + // lower right corner + GLEXT_glMultiTexCoord2fvARB(GL_TEXTURE0_ARB,(const GLfloat*)&c1->t[3]); + GLEXT_glMultiTexCoord2fvARB(GL_TEXTURE1_ARB,(const GLfloat*)&c2->t[3]); + glVertex3fv((const GLfloat*)&c1->v[3]); + + glEnd(); +} + +void gld_PreprocessDetail(void) +{ + if (gl_arb_multitexture) + { + GLEXT_glClientActiveTextureARB(GL_TEXTURE0_ARB); +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + glTexCoordPointer(2, GL_FLOAT, sizeof(flats_vbo[0]), flats_vbo_u); +#endif + + GLEXT_glClientActiveTextureARB(GL_TEXTURE1_ARB); +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + glTexCoordPointer(2, GL_FLOAT, sizeof(flats_vbo[0]), flats_vbo_u); +#endif + GLEXT_glClientActiveTextureARB(GL_TEXTURE0_ARB); + + GLEXT_glActiveTextureARB(GL_TEXTURE1_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 2); + GLEXT_glActiveTextureARB(GL_TEXTURE0_ARB); + } +} + + +void gld_EnableDetail(int enable) +{ + if (!gl_arb_multitexture || !render_usedetail) + return; + + gld_EnableTexture2D(GL_TEXTURE1_ARB, enable); + gld_EnableClientCoordArray(GL_TEXTURE1_ARB, enable); +} + +void gld_DrawWallWithDetail(GLWall *wall) +{ + float w, h, dx, dy; + dboolean fake = (wall->flag == GLDWF_TOPFLUD) || (wall->flag == GLDWF_BOTFLUD); + detail_t *detail = wall->gltexture->detail; + + w = wall->gltexture->detail_width; + h = wall->gltexture->detail_height; + dx = detail->offsetx; + dy = detail->offsety; + + if (fake) + { + int i; + gl_strip_coords_t c1; + gl_strip_coords_t c2; + + gld_BindFlat(wall->gltexture, 0); + + gld_SetupFloodStencil(wall); + gld_SetupFloodedPlaneLight(wall); + gld_SetupFloodedPlaneCoords(wall, &c1); + for (i = 0; i < 4; i++) + { + c2.t[i][0] = c1.t[i][0] * w + dx; + c2.t[i][1] = c1.t[i][1] * h + dy; + } + + gld_EnableTexture2D(GL_TEXTURE1_ARB, true); + gld_DrawTriangleStripARB(wall, &c1, &c2); + gld_EnableTexture2D(GL_TEXTURE1_ARB, false); + + gld_ClearFloodStencil(wall); + } + else + { + gld_StaticLightAlpha(wall->light, wall->alpha); + glBegin(GL_TRIANGLE_FAN); + + // lower left corner + GLEXT_glMultiTexCoord2fARB(GL_TEXTURE0_ARB,wall->ul,wall->vb); + GLEXT_glMultiTexCoord2fARB(GL_TEXTURE1_ARB,wall->ul*w+dx,wall->vb*h+dy); + glVertex3f(wall->glseg->x1,wall->ybottom,wall->glseg->z1); + + // split left edge of wall + if (!wall->glseg->fracleft) + gld_SplitLeftEdge(wall, true); + + // upper left corner + GLEXT_glMultiTexCoord2fARB(GL_TEXTURE0_ARB,wall->ul,wall->vt); + GLEXT_glMultiTexCoord2fARB(GL_TEXTURE1_ARB,wall->ul*w+dx,wall->vt*h+dy); + glVertex3f(wall->glseg->x1,wall->ytop,wall->glseg->z1); + + // upper right corner + GLEXT_glMultiTexCoord2fARB(GL_TEXTURE0_ARB,wall->ur,wall->vt); + GLEXT_glMultiTexCoord2fARB(GL_TEXTURE1_ARB,wall->ur*w+dx,wall->vt*h+dy); + glVertex3f(wall->glseg->x2,wall->ytop,wall->glseg->z2); + + // split right edge of wall + if (!wall->glseg->fracright) + gld_SplitRightEdge(wall, true); + + // lower right corner + GLEXT_glMultiTexCoord2fARB(GL_TEXTURE0_ARB,wall->ur,wall->vb); + GLEXT_glMultiTexCoord2fARB(GL_TEXTURE1_ARB,wall->ur*w+dx,wall->vb*h+dy); + glVertex3f(wall->glseg->x2,wall->ybottom,wall->glseg->z2); + + glEnd(); + } +} + +void gld_DrawWallDetail_NoARB(GLWall *wall) +{ + if (!wall->gltexture->detail) + return; + + if (wall->flag >= GLDWF_SKY) + return; + + if (gld_IsDetailVisible(xCamera, yCamera, + wall->glseg->x1, wall->glseg->z1, + wall->glseg->x2, wall->glseg->z2)) + { + float w, h, dx, dy; + dboolean fake = (wall->flag == GLDWF_TOPFLUD) || (wall->flag == GLDWF_BOTFLUD); + detail_t *detail = wall->gltexture->detail; + + w = wall->gltexture->detail_width; + h = wall->gltexture->detail_height; + dx = detail->offsetx; + dy = detail->offsety; + + gld_BindDetail(wall->gltexture, detail->texid); + + if (fake) + { + int i; + gl_strip_coords_t c; + + if (gl_use_fog) + { + // calculation of fog density for flooded walls + if (wall->seg->backsector) + { + wall->fogdensity = gld_CalcFogDensity(wall->seg->frontsector, + wall->seg->backsector->lightlevel, GLDIT_FWALL); + } + gld_SetFog(wall->fogdensity); + } + + gld_SetupFloodStencil(wall); + gld_SetupFloodedPlaneLight(wall); + gld_SetupFloodedPlaneCoords(wall, &c); + for (i = 0; i < 4; i++) + { + c.t[i][0] = c.t[i][0] * w + dx; + c.t[i][1] = c.t[i][1] * h + dy; + } + gld_DrawTriangleStrip(wall, &c); + gld_ClearFloodStencil(wall); + } + else + { + gld_StaticLightAlpha(wall->light, wall->alpha); + glBegin(GL_TRIANGLE_FAN); + + // lower left corner + glTexCoord2f(wall->ul*w+dx,wall->vb*h+dy); + glVertex3f(wall->glseg->x1,wall->ybottom,wall->glseg->z1); + + // split left edge of wall + if (!wall->glseg->fracleft) + gld_SplitLeftEdge(wall, true); + + // upper left corner + glTexCoord2f(wall->ul*w+dx,wall->vt*h+dy); + glVertex3f(wall->glseg->x1,wall->ytop,wall->glseg->z1); + + // upper right corner + glTexCoord2f(wall->ur*w+dx,wall->vt*h+dy); + glVertex3f(wall->glseg->x2,wall->ytop,wall->glseg->z2); + + // split right edge of wall + if (!wall->glseg->fracright) + gld_SplitRightEdge(wall, true); + + // lower right corner + glTexCoord2f(wall->ur*w+dx,wall->vb*h+dy); + glVertex3f(wall->glseg->x2,wall->ybottom,wall->glseg->z2); + + glEnd(); + } + } +} + +void gld_DrawFlatDetail_NoARB(GLFlat *flat) +{ + float w, h, dx, dy; + int loopnum; + GLLoopDef *currentloop; + detail_t *detail; + + if (!flat->gltexture->detail) + return; + + detail = flat->gltexture->detail; + gld_BindDetail(flat->gltexture, detail->texid); + + gld_StaticLightAlpha(flat->light, flat->alpha); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glTranslatef(0.0f,flat->z,0.0f); + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + + w = flat->gltexture->detail_width; + h = flat->gltexture->detail_height; + dx = detail->offsetx; + dy = detail->offsety; + + if ((flat->flags & GLFLAT_HAVE_OFFSET) || dx || dy) + { + glTranslatef(flat->uoffs * w + dx, flat->voffs * h + dy, 0.0f); + } + + glScalef(w, h, 1.0f); + + if (flat->sectornum>=0) + { + // go through all loops of this sector +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + if (gl_use_display_lists) + { + glCallList(flats_display_list + flat->sectornum); + } + else + { + for (loopnum=0; loopnumsectornum].loopcount; loopnum++) + { + currentloop=§orloops[flat->sectornum].loops[loopnum]; + glDrawArrays(currentloop->mode,currentloop->vertexindex,currentloop->vertexcount); + } + } +#else + for (loopnum=0; loopnumsectornum].loopcount; loopnum++) + { + int vertexnum; + // set the current loop + currentloop=§orloops[flat->sectornum].loops[loopnum]; + if (!currentloop) + continue; + // set the mode (GL_TRIANGLES, GL_TRIANGLE_STRIP or GL_TRIANGLE_FAN) + glBegin(currentloop->mode); + // go through all vertexes of this loop + for (vertexnum=currentloop->vertexindex; vertexnum<(currentloop->vertexindex+currentloop->vertexcount); vertexnum++) + { + // set texture coordinate of this vertex + if (true) + { + GLEXT_glMultiTexCoord2fvARB(GL_TEXTURE0_ARB, (GLfloat*)&flats_vbo[vertexnum].u); + GLEXT_glMultiTexCoord2fvARB(GL_TEXTURE1_ARB, (GLfloat*)&flats_vbo[vertexnum].u); + } + else + { + glTexCoord2fv((GLfloat*)&flats_vbo[vertexnum].u); + } + // set vertex coordinate + glVertex3fv((GLfloat*)&flats_vbo[vertexnum].x); + } + // end of loop + glEnd(); + } +#endif + } + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +} + +static int C_DECL dicmp_wall_detail(const void *a, const void *b) +{ + detail_t *d1 = ((const GLDrawItem *)a)->item.wall->gltexture->detail; + detail_t *d2 = ((const GLDrawItem *)b)->item.wall->gltexture->detail; + return d1 - d2; +} + +static int C_DECL dicmp_flat_detail(const void *a, const void *b) +{ + detail_t *d1 = ((const GLDrawItem *)a)->item.flat->gltexture->detail; + detail_t *d2 = ((const GLDrawItem *)b)->item.flat->gltexture->detail; + return d1 - d2; +} + +void gld_DrawItemsSortByDetail(GLDrawItemType itemtype) +{ + typedef int(C_DECL *DICMP_ITEM)(const void *a, const void *b); + + static DICMP_ITEM itemfuncs[GLDIT_TYPES] = { + 0, + dicmp_wall_detail, dicmp_wall_detail, dicmp_wall_detail, dicmp_wall_detail, dicmp_wall_detail, + dicmp_wall_detail, dicmp_wall_detail, + dicmp_flat_detail, dicmp_flat_detail, + dicmp_flat_detail, dicmp_flat_detail, + 0, 0, 0, + 0, + }; + + if (itemfuncs[itemtype] && gld_drawinfo.num_items[itemtype] > 1) + { + qsort(gld_drawinfo.items[itemtype], gld_drawinfo.num_items[itemtype], + sizeof(gld_drawinfo.items[itemtype]), itemfuncs[itemtype]); + } +} + +void gld_DrawDetail_NoARB(void) +{ + int i; + + if (!scene_has_wall_details && !scene_has_flat_details) + return; + + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); + glBlendFunc (GL_DST_COLOR, GL_SRC_COLOR); + + last_detail_texid = -1; + + // detail flats + + if (scene_has_flat_details) + { + // enable backside removing + glEnable(GL_CULL_FACE); + + // floors + glCullFace(GL_FRONT); + gld_DrawItemsSortByDetail(GLDIT_FLOOR); + for (i = gld_drawinfo.num_items[GLDIT_FLOOR] - 1; i >= 0; i--) + { + gld_SetFog(gld_drawinfo.items[GLDIT_FLOOR][i].item.flat->fogdensity); + gld_DrawFlatDetail_NoARB(gld_drawinfo.items[GLDIT_FLOOR][i].item.flat); + } + // ceilings + glCullFace(GL_BACK); + gld_DrawItemsSortByDetail(GLDIT_CEILING); + for (i = gld_drawinfo.num_items[GLDIT_CEILING] - 1; i >= 0; i--) + { + gld_SetFog(gld_drawinfo.items[GLDIT_CEILING][i].item.flat->fogdensity); + gld_DrawFlatDetail_NoARB(gld_drawinfo.items[GLDIT_CEILING][i].item.flat); + } + glDisable(GL_CULL_FACE); + } + + // detail walls + if (scene_has_wall_details) + { + gld_DrawItemsSortByDetail(GLDIT_WALL); + for (i = gld_drawinfo.num_items[GLDIT_WALL] - 1; i >= 0; i--) + { + gld_SetFog(gld_drawinfo.items[GLDIT_WALL][i].item.wall->fogdensity); + gld_DrawWallDetail_NoARB(gld_drawinfo.items[GLDIT_WALL][i].item.wall); + } + + if (!gl_use_stencil) + { + gld_DrawItemsSortByDetail(GLDIT_MWALL); + } + + for (i = gld_drawinfo.num_items[GLDIT_MWALL] - 1; i >= 0; i--) + { + GLWall *wall = gld_drawinfo.items[GLDIT_MWALL][i].item.wall; + if (gl_use_stencil) + { + if (!(wall->gltexture->flags & GLTEXTURE_HASHOLES)) + { + gld_SetFog(wall->fogdensity); + gld_DrawWallDetail_NoARB(wall); + } + } + else + { + gld_SetFog(wall->fogdensity); + gld_DrawWallDetail_NoARB(wall); + } + } + + if (gld_drawinfo.num_items[GLDIT_FWALL] > 0) + { + glPolygonOffset(1.0f, 128.0f); + glEnable(GL_POLYGON_OFFSET_FILL); + glEnable(GL_STENCIL_TEST); + + gld_DrawItemsSortByDetail(GLDIT_FWALL); + for (i = gld_drawinfo.num_items[GLDIT_FWALL] - 1; i >= 0; i--) + { + gld_DrawWallDetail_NoARB(gld_drawinfo.items[GLDIT_FWALL][i].item.wall); + } + + glDisable(GL_STENCIL_TEST); + glPolygonOffset(0.0f, 0.0f); + glDisable(GL_POLYGON_OFFSET_FILL); + } + + gld_DrawItemsSortByDetail(GLDIT_TWALL); + for (i = gld_drawinfo.num_items[GLDIT_TWALL] - 1; i >= 0; i--) + { + gld_SetFog(gld_drawinfo.items[GLDIT_TWALL][i].item.wall->fogdensity); + gld_DrawWallDetail_NoARB(gld_drawinfo.items[GLDIT_TWALL][i].item.wall); + } + } + + // restore + glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void gld_InitFrameDetails(void) +{ + last_detail_texid = -1; + + scene_has_details = + (render_usedetail) && + (scene_has_wall_details || scene_has_flat_details); +} + +void gld_BindDetailARB(GLTexture *gltexture, int enable) +{ + if (scene_has_details) + { + gld_EnableTexture2D(GL_TEXTURE1_ARB, enable); + gld_EnableClientCoordArray(GL_TEXTURE1_ARB, enable); + + if (enable && + gltexture->detail && + gltexture->detail->texid != last_detail_texid) + { + last_detail_texid = gltexture->detail->texid; + + GLEXT_glActiveTextureARB(GL_TEXTURE1_ARB); + glBindTexture(GL_TEXTURE_2D, gltexture->detail->texid); + GLEXT_glActiveTextureARB(GL_TEXTURE0_ARB); + } + } +} + +void gld_BindDetail(GLTexture *gltexture, int enable) +{ + if (scene_has_details) + { + if (enable && + gltexture->detail && + gltexture->detail->texid != last_detail_texid) + { + last_detail_texid = gltexture->detail->texid; + glBindTexture(GL_TEXTURE_2D, gltexture->detail->texid); + } + } +} + +void gld_SetTexDetail(GLTexture *gltexture) +{ + int i; + + gltexture->detail = NULL; + + if (details_count > 0) + { + // linear search + for (i = 0; i < details_count; i++) + { + if (gltexture->index == details[i].texture_num) + { + gltexture->detail = &details[i]; + break; + } + } + + if (!gltexture->detail) + { + switch (gltexture->textype) + { + case GLDT_TEXTURE: + if (details[TAG_DETAIL_WALL].texid > 0) + gltexture->detail = &details[TAG_DETAIL_WALL]; + break; + case GLDT_FLAT: + if (details[TAG_DETAIL_FLAT].texid > 0) + gltexture->detail = &details[TAG_DETAIL_FLAT]; + break; + } + } + + if (gltexture->detail) + { + gltexture->detail_width = (float)gltexture->realtexwidth / gltexture->detail->width; + gltexture->detail_height = (float)gltexture->realtexheight / gltexture->detail->height; + } + } +} + +GLuint gld_LoadDetailName(const char *name) +{ + GLuint texid = 0; + int lump; + + lump = (W_CheckNumForName)(name, ns_hires); + + if (lump != -1) + { + SDL_PixelFormat fmt; + SDL_Surface *surf = NULL; + SDL_Surface *surf_raw; + +#ifdef HAVE_LIBSDL2_IMAGE + surf_raw = IMG_Load_RW(SDL_RWFromConstMem(W_CacheLumpNum(lump), W_LumpLength(lump)), 1); +#else + surf_raw = SDL_LoadBMP_RW(SDL_RWFromConstMem(W_CacheLumpNum(lump), W_LumpLength(lump)), 1); +#endif + + W_UnlockLumpNum(lump); + + if (surf_raw) + { + fmt = *surf_raw->format; + fmt.BitsPerPixel = 24; + fmt.BytesPerPixel = 3; + surf = SDL_ConvertSurface(surf_raw, &fmt, surf_raw->flags); + SDL_FreeSurface(surf_raw); + if (surf) + { + if (gl_arb_multitexture) + GLEXT_glActiveTextureARB(GL_TEXTURE1_ARB); + glGenTextures(1, &texid); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glBindTexture(GL_TEXTURE_2D, texid); + + gluBuild2DMipmaps(GL_TEXTURE_2D, gl_tex_format, + surf->w, surf->h, + imageformats[surf->format->BytesPerPixel], + GL_UNSIGNED_BYTE, surf->pixels); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + if (gl_ext_texture_filter_anisotropic) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (GLfloat)(1<texid = gld_LoadDetailName(sc_String); + + if (detail->texid > 0) + { + float f; + + if (SC_Check() && SC_GetString() && M_StrToFloat(sc_String, &f)) + detail->width = f; + if (SC_Check() && SC_GetString() && M_StrToFloat(sc_String, &f)) + detail->height = f; + + if (SC_Check() && SC_GetString() && M_StrToFloat(sc_String, &f)) + detail->offsetx = f / detail->width; + if (SC_Check() && SC_GetString() && M_StrToFloat(sc_String, &f)) + detail->offsety = f / detail->height; + + result = true; + } + } + // skip the rest of unknown params + while (SC_Check()) + SC_GetString(); + } + + return result; +} + +void gld_ParseDetailItem(tag_detail_e item) +{ + // item's default values + details[item].width = 16.0f; + details[item].height = 16.0f; + details[item].offsetx = 0.0f; + details[item].offsety = 0.0f; + if (SC_Check() && !SC_Compare("{")) + { + gld_ReadDetailParams(item, &details[item]); + } + + if (SC_GetString() && SC_Compare("{")) + { + while (SC_GetString() && !SC_Compare("}")) + { + int result; + detail_t detail; + + // reset fields for next iteration + detail.texid = 0; + detail.width = 16.0f; + detail.height = 16.0f; + detail.offsetx = 0.0f; + detail.offsety = 0.0f; + + if (strlen(sc_String) < 9) + { + switch (item) + { + case TAG_DETAIL_WALL: + detail.texture_num = R_CheckTextureNumForName(sc_String); + break; + case TAG_DETAIL_FLAT: + detail.texture_num = (W_CheckNumForName)(sc_String, ns_flats); + break; + } + + result = gld_ReadDetailParams(item, &detail); + + if (result || details[item].texid > 0) + { + if (details_count + 1 > details_size) + { + details_size = (details_size == 0 ? 128 : details_size * 2); + details = realloc(details, details_size * sizeof(details[0])); + } + details[details_count] = detail; + details_count++; + } + } + } + } +} + +void gld_ParseDetail(void) +{ + gld_ShutdownDetail(); + + details_count = 2; // reserved for default wall and flat + details_size = 128; + details = calloc(details_size, sizeof(details[0])); + + // skip "Detail" params + while (SC_Check() && !SC_Compare("{")) + SC_GetString(); + + if (SC_GetString() && SC_Compare("{")) + { + while (SC_GetString() && !SC_Compare("}")) + { + switch (SC_MatchString(DetailItem_Keywords)) + { + case TAG_DETAIL_WALL: + gld_ParseDetailItem(TAG_DETAIL_WALL); + break; + case TAG_DETAIL_FLAT: + gld_ParseDetailItem(TAG_DETAIL_FLAT); + break; + } + } + } +} diff --git a/src/gl_drawinfo.c b/src/gl_drawinfo.c new file mode 100644 index 0000000..b9f84fa --- /dev/null +++ b/src/gl_drawinfo.c @@ -0,0 +1,169 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + * memory manager for GL data + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomtype.h" +#include "gl_intern.h" +#include "lprintf.h" + +GLDrawInfo gld_drawinfo; + +// +// gld_FreeDrawInfo +// +void gld_FreeDrawInfo(void) +{ + int i; + + for (i = 0; i < gld_drawinfo.maxsize; i++) + { + if (gld_drawinfo.data[i].data) + { + free(gld_drawinfo.data[i].data); + gld_drawinfo.data[i].data = 0; + } + } + free(gld_drawinfo.data); + gld_drawinfo.data = 0; + + for (i = 0; i < GLDIT_TYPES; i++) + { + if (gld_drawinfo.items[i]) + { + free(gld_drawinfo.items[i]); + gld_drawinfo.items[i] = 0; + } + } + + memset(&gld_drawinfo, 0, sizeof(GLDrawInfo)); +} + +// +// gld_ResetDrawInfo +// +// Should be used between frames (in gld_StartDrawScene) +// +void gld_ResetDrawInfo(void) +{ + int i; + + for (i = 0; i < gld_drawinfo.maxsize; i++) + { + gld_drawinfo.data[i].size = 0; + } + gld_drawinfo.size = 0; + + for (i = 0; i < GLDIT_TYPES; i++) + { + gld_drawinfo.num_items[i] = 0; + } +} + +// +// gld_AddDrawRange +// +static void gld_AddDrawRange(int size) +{ + gld_drawinfo.maxsize++; + gld_drawinfo.data = realloc(gld_drawinfo.data, + gld_drawinfo.maxsize * sizeof(gld_drawinfo.data[0])); + + gld_drawinfo.data[gld_drawinfo.size].maxsize = size; + gld_drawinfo.data[gld_drawinfo.size].data = malloc(size); + gld_drawinfo.data[gld_drawinfo.size].size = 0; +} + +// +// gld_AddDrawItem +// +#define NEWSIZE (MAX(64 * 1024, itemsize)) +#define SIZEOF8(type) ((sizeof(type)+7)&~7) +void gld_AddDrawItem(GLDrawItemType itemtype, void *itemdata) +{ + int itemsize = 0; + byte *item_p = NULL; + + static int itemsizes[GLDIT_TYPES] = { + 0, + SIZEOF8(GLWall), SIZEOF8(GLWall), SIZEOF8(GLWall), SIZEOF8(GLWall), SIZEOF8(GLWall), + SIZEOF8(GLWall), SIZEOF8(GLWall), + SIZEOF8(GLFlat), SIZEOF8(GLFlat), + SIZEOF8(GLFlat), SIZEOF8(GLFlat), + SIZEOF8(GLSprite), SIZEOF8(GLSprite), SIZEOF8(GLSprite), + SIZEOF8(GLShadow), + SIZEOF8(GLHealthBar) + }; + + itemsize = itemsizes[itemtype]; + if (itemsize == 0) + { + I_Error("gld_AddDrawItem: unknown GLDrawItemType %d", itemtype); + } + + if (gld_drawinfo.maxsize == 0) + { + gld_AddDrawRange(NEWSIZE); + } + + if (gld_drawinfo.data[gld_drawinfo.size].size + itemsize >= + gld_drawinfo.data[gld_drawinfo.size].maxsize) + { + gld_drawinfo.size++; + if (gld_drawinfo.size >= gld_drawinfo.maxsize) + { + gld_AddDrawRange(NEWSIZE); + } + } + + item_p = gld_drawinfo.data[gld_drawinfo.size].data + + gld_drawinfo.data[gld_drawinfo.size].size; + + memcpy(item_p, itemdata, itemsize); + + gld_drawinfo.data[gld_drawinfo.size].size += itemsize; + + if (gld_drawinfo.num_items[itemtype] >= gld_drawinfo.max_items[itemtype]) + { + gld_drawinfo.max_items[itemtype] += 64; + gld_drawinfo.items[itemtype] = realloc( + gld_drawinfo.items[itemtype], + gld_drawinfo.max_items[itemtype] * sizeof(gld_drawinfo.items[0][0])); + } + + gld_drawinfo.items[itemtype][gld_drawinfo.num_items[itemtype]].item.item = item_p; + gld_drawinfo.num_items[itemtype]++; +} +#undef SIZEOF8 +#undef NEWSIZE diff --git a/src/gl_fbo.c b/src/gl_fbo.c new file mode 100644 index 0000000..007c4e8 --- /dev/null +++ b/src/gl_fbo.c @@ -0,0 +1,183 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Thanks Roman "Vortex" Marchenko + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gl_opengl.h" + +#include + +#include "gl_intern.h" + +#include "i_main.h" +#include "lprintf.h" + +dboolean gl_use_FBO = false; + +#ifdef USE_FBO_TECHNIQUE +GLuint glSceneImageFBOTexID = 0; +GLuint glDepthBufferFBOTexID = 0; +GLuint glSceneImageTextureFBOTexID = 0; +int SceneInTexture = false; +static dboolean gld_CreateScreenSizeFBO(void); +#endif + +//e6y: motion bloor +int gl_motionblur; +int gl_use_motionblur = false; +motion_blur_params_t motion_blur; + +#ifdef USE_FBO_TECHNIQUE + +void gld_InitMotionBlur(void); + +void gld_InitFBO(void) +{ + gld_FreeScreenSizeFBO(); + + gl_use_motionblur = gl_ext_framebuffer_object && gl_motionblur && gl_ext_blend_color; + + gl_use_FBO = (gl_ext_framebuffer_object) && (gl_version >= OPENGL_VERSION_1_3) && + (gl_use_motionblur || !gl_boom_colormaps || gl_has_hires); + + if (gl_use_FBO) + { + if (gld_CreateScreenSizeFBO()) + { + // motion blur setup + gld_InitMotionBlur(); + } + else + { + gld_FreeScreenSizeFBO(); + gl_use_FBO = false; + gl_ext_framebuffer_object = false; + } + } +} + +static dboolean gld_CreateScreenSizeFBO(void) +{ + int status = 0; + GLenum internalFormat; + dboolean attach_stencil = gl_ext_packed_depth_stencil;// && (gl_has_hires || gl_use_motionblur); + + if (!gl_ext_framebuffer_object) + return false; + + GLEXT_glGenFramebuffersEXT(1, &glSceneImageFBOTexID); + GLEXT_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, glSceneImageFBOTexID); + + GLEXT_glGenRenderbuffersEXT(1, &glDepthBufferFBOTexID); + GLEXT_glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, glDepthBufferFBOTexID); + + internalFormat = (attach_stencil ? GL_DEPTH_STENCIL_EXT : GL_DEPTH_COMPONENT); + GLEXT_glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, internalFormat, SCREENWIDTH, SCREENHEIGHT); + + // attach a renderbuffer to depth attachment point + GLEXT_glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, glDepthBufferFBOTexID); + + if (attach_stencil) + { + // attach a renderbuffer to stencil attachment point + GLEXT_glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, glDepthBufferFBOTexID); + } + + glGenTextures(1, &glSceneImageTextureFBOTexID); + glBindTexture(GL_TEXTURE_2D, glSceneImageTextureFBOTexID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCREENWIDTH, SCREENHEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + // e6y + // Some ATIs drivers have a bug whereby adding the depth renderbuffer + // and then a texture causes the application to crash. + // This should be kept in mind when doing any FBO related work and + // tested for as it is possible it could be fixed in a future driver revision + // thus rendering the problem non-existent. + PRBOOM_TRY(EXEPTION_glFramebufferTexture2DEXT) + { + GLEXT_glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, glSceneImageTextureFBOTexID, 0); + status = GLEXT_glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + } + PRBOOM_EXCEPT(EXEPTION_glFramebufferTexture2DEXT) + + if (status == GL_FRAMEBUFFER_COMPLETE_EXT) + { + GLEXT_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + } + else + { + lprintf(LO_ERROR, "gld_CreateScreenSizeFBO: Cannot create framebuffer object (error code: %d)\n", status); + } + + return (status == GL_FRAMEBUFFER_COMPLETE_EXT); +} + +void gld_FreeScreenSizeFBO(void) +{ + if (!gl_ext_framebuffer_object) + return; + + GLEXT_glDeleteFramebuffersEXT(1, &glSceneImageFBOTexID); + glSceneImageFBOTexID = 0; + + GLEXT_glDeleteRenderbuffersEXT(1, &glDepthBufferFBOTexID); + glDepthBufferFBOTexID = 0; + + glDeleteTextures(1, &glSceneImageTextureFBOTexID); + glSceneImageTextureFBOTexID = 0; +} + +void gld_InitMotionBlur(void) +{ + if (gl_use_motionblur) + { + float f; + + sscanf(motion_blur.str_min_speed, "%f", &f); + motion_blur.minspeed_pow2 = f * f; + + sscanf(motion_blur.str_min_angle, "%f", &f); + motion_blur.minangle = (int)(f * 65536.0f / 360.0f); + + sscanf(motion_blur.str_att_a, "%f", &motion_blur.att_a); + sscanf(motion_blur.str_att_b, "%f", &motion_blur.att_b); + sscanf(motion_blur.str_att_c, "%f", &motion_blur.att_c); + } +} +#endif diff --git a/src/gl_gamma.c b/src/gl_gamma.c new file mode 100644 index 0000000..6de22ce --- /dev/null +++ b/src/gl_gamma.c @@ -0,0 +1,202 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include "doomstat.h" +#include "v_video.h" +#include "gl_intern.h" +#include "doomtype.h" +#include "i_video.h" +#include "m_argv.h" +#include "lprintf.h" + +#ifndef HIBYTE +#define HIBYTE(W) (((W) >> 8) & 0xFF) +#endif + +int useglgamma; +int gl_DeviceSupportsGamma = false; + +static Uint16 gl_oldHardwareGamma[3][256]; + +// +// gld_CheckHardwareGamma +// +// Determines if the underlying hardware supports the Win32 gamma correction API. +// +void gld_CheckHardwareGamma(void) +{ + gl_DeviceSupportsGamma = (-1 != SDL_GetWindowGammaRamp(sdl_window, gl_oldHardwareGamma[0], gl_oldHardwareGamma[1], gl_oldHardwareGamma[2])); + + if (gl_DeviceSupportsGamma) + { + // + // do a sanity check on the gamma values + // + if ( + (HIBYTE(gl_oldHardwareGamma[0][255]) <= HIBYTE(gl_oldHardwareGamma[0][0])) || + (HIBYTE(gl_oldHardwareGamma[1][255]) <= HIBYTE(gl_oldHardwareGamma[1][0])) || + (HIBYTE(gl_oldHardwareGamma[2][255]) <= HIBYTE(gl_oldHardwareGamma[2][0]))) + { + gl_DeviceSupportsGamma = false; + } + + // + // make sure that we didn't have a prior crash in the game, and if so we need to + // restore the gamma values to at least a linear value + // + if ((HIBYTE(gl_oldHardwareGamma[0][181]) == 255)) + //if ((HIBYTE(gl_oldHardwareGamma[0][247]) == 255)) + { + int g; + + lprintf(LO_WARN, "gld_CheckHardwareGamma: suspicious gamma tables, using linear ramp for restoration\n"); + + for ( g = 0; g < 255; g++ ) + { + gl_oldHardwareGamma[0][g] = g << 8; + gl_oldHardwareGamma[1][g] = g << 8; + gl_oldHardwareGamma[2][g] = g << 8; + } + } + + } + + if (!gl_DeviceSupportsGamma) + { + lprintf(LO_WARN, "gld_CheckHardwareGamma: device has broken gamma support\n"); + } +} + +// +// gld_SetGammaRamp +// +// This routine should only be called if gl_DeviceSupportsGamma is TRUE +// +int gld_SetGammaRamp(int gamma) +{ + int succeeded = false; + static int first = true; + float g = (BETWEEN(0, MAX_GLGAMMA, gamma)) / 10.0f + 1.0f; + Uint16 gammatable[256]; + + if (!gl_DeviceSupportsGamma) + return false; + + if (gamma == -1) + { + succeeded = (SDL_SetWindowGammaRamp(sdl_window, gl_oldHardwareGamma[0], gl_oldHardwareGamma[1], gl_oldHardwareGamma[2]) != -1); + } + else + { + if (first && desired_fullscreen) + { + // From GZDoom: + // + // Fix for Radeon 9000, possibly other R200s: When the device is + // reset, it resets the gamma ramp, but the driver apparently keeps a + // cached copy of the ramp that it doesn't update, so when + // SetGammaRamp is called later to handle the NeedGammaUpdate flag, + // it doesn't do anything, because the gamma ramp is the same as the + // one passed in the last call, even though the visible gamma ramp + // actually has changed. + // + // So here we force the gamma ramp to something absolutely horrible and + // trust that we will be able to properly set the gamma later + first = false; + memset(gammatable, 0, sizeof(gammatable)); + SDL_SetWindowGammaRamp(sdl_window, NULL, NULL, gammatable); + } + + SDL_CalculateGammaRamp(g, gammatable); + + // has no effect sometimes on Intel Graphics + // do it twice! + SDL_SetWindowGammaRamp(sdl_window, gammatable, gammatable, gammatable); + succeeded = (SDL_SetWindowGammaRamp(sdl_window, gammatable, gammatable, gammatable) != -1); + if (!succeeded) + { + lprintf(LO_WARN, "gld_SetGammaRamp: hardware gamma adjustment is not supported\n"); + gl_lightmode = gl_lightmode_glboom; + } + } + + return succeeded; +} + +// gld_ResetGammaRamp +// Restoring the gamma values to a linear value and exit +void gld_ResetGammaRamp(void) +{ + if (M_CheckParm("-resetgamma")) + { + if (gld_SetGammaRamp(1)) + { + lprintf(LO_WARN, "gld_ResetGammaRamp: suspicious gamma tables, using linear ramp for restoration\n"); + _exit(0); + } + } +} + +void gld_ApplyGammaRamp(byte *buf, int pitch, int width, int height) +{ + if (gl_hardware_gamma) + { + int w, h; + byte *pixel; + Uint16 r[256], g[256], b[256]; + + SDL_GetWindowGammaRamp(sdl_window, &r[0], &g[0], &b[0]); + + for (h = 0; h < height; h++) + { + for (w = 0; w < width; w++) + { + pixel = buf + h * pitch + 3 * w; + + *(pixel + 0) = (byte)(r[*(pixel + 0)] >> 8); + *(pixel + 1) = (byte)(g[*(pixel + 1)] >> 8); + *(pixel + 2) = (byte)(b[*(pixel + 2)] >> 8); + } + } + } +} diff --git a/src/gl_hires.c b/src/gl_hires.c new file mode 100644 index 0000000..60c864f --- /dev/null +++ b/src/gl_hires.c @@ -0,0 +1,1477 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gl_opengl.h" + +#ifdef _MSC_VER +//#include /* needed for DirectX's DDSURFACEDESC2 structure definition */ +#include +#else +#include +#endif +#include +#include +#ifdef HAVE_LIBSDL2_IMAGE +#include +#endif +#include "doomstat.h" +#include "v_video.h" +#include "gl_intern.h" +#include "i_system.h" +#include "w_wad.h" +#include "lprintf.h" +#include "i_video.h" +#include "hu_lib.h" +#include "hu_stuff.h" +#include "r_main.h" +#include "r_sky.h" +#include "m_argv.h" +#include "m_misc.h" +#include "e6y.h" + +#include "m_io.h" + +unsigned int gl_has_hires = 0; +int gl_texture_external_hires = -1; +int gl_texture_internal_hires = -1; +int gl_hires_override_pwads; +const char *gl_texture_hires_dir = NULL; +int gl_hires_24bit_colormap = false; + +static GLuint progress_texid = 0; +static unsigned int lastupdate = 0; + +int gld_ProgressStart(void) +{ + if (!progress_texid) + { + progress_texid = CaptureScreenAsTexID(); + lastupdate = SDL_GetTicks() - 100; + return true; + } + + return false; +} + +int gld_ProgressRestoreScreen(void) +{ + int total_w, total_h; + float fU1, fU2, fV1, fV2; + + if (progress_texid) + { + total_w = gld_GetTexDimension(SCREENWIDTH); + total_h = gld_GetTexDimension(SCREENHEIGHT); + + fU1 = 0.0f; + fV1 = (float)SCREENHEIGHT / (float)total_h; + fU2 = (float)SCREENWIDTH / (float)total_w; + fV2 = 0.0f; + + gld_EnableTexture2D(GL_TEXTURE0_ARB, true); + + glBindTexture(GL_TEXTURE_2D, progress_texid); + glColor3f(1.0f, 1.0f, 1.0f); + + glBegin(GL_TRIANGLE_STRIP); + { + glTexCoord2f(fU1, fV1); glVertex2f(0.0f, 0.0f); + glTexCoord2f(fU1, fV2); glVertex2f(0.0f, (float)SCREENHEIGHT); + glTexCoord2f(fU2, fV1); glVertex2f((float)SCREENWIDTH, 0.0f); + glTexCoord2f(fU2, fV2); glVertex2f((float)SCREENWIDTH, (float)SCREENHEIGHT); + } + glEnd(); + + return true; + } + + return false; +} + +int gld_ProgressEnd(void) +{ + if (progress_texid != 0) + { + gld_ProgressRestoreScreen(); + I_FinishUpdate(); + gld_ProgressRestoreScreen(); + glDeleteTextures(1, &progress_texid); + progress_texid = 0; + return true; + } + + return false; +} + +void gld_ProgressUpdate(const char * text, int progress, int total) +{ + int len; + static char last_text[32] = {0}; + unsigned int tic; + + if (!progress_texid) + return; + + // do not do it often + tic = SDL_GetTicks(); + if (tic - lastupdate < 100) + return; + lastupdate = tic; + + if ((text) && (strlen(text) > 0) && strcmp((last_text[0] ? last_text : ""), text)) + { + const char *s; + strcpy(last_text, text); + + if (!w_precache.f) + HU_Start(); + + HUlib_clearTextLine(&w_precache); + s = text; + while (*s) + HUlib_addCharToTextLine(&w_precache, *(s++)); + HUlib_setTextXCenter(&w_precache); + } + + gld_ProgressRestoreScreen(); + HUlib_drawTextLine(&w_precache, false); + + len = MIN(SCREENWIDTH, (int)((int_64_t)SCREENWIDTH * progress / total)); + V_FillRect(0, 0, SCREENHEIGHT - 4, len - 0, 4, 4); + if (len > 4) + { + V_FillRect(0, 2, SCREENHEIGHT - 3, len - 4, 2, 31); + } + + I_FinishUpdate(); +} + +#ifdef HAVE_LIBSDL2_IMAGE + +static const char* gld_HiRes_GetInternalName(GLTexture *gltexture); +static int gld_HiRes_GetExternalName(GLTexture *gltexture, char *img_path, char *dds_path); +static void gld_HiRes_Bind(GLTexture *gltexture, GLuint *glTexID); + +#define DDRAW_H_MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((unsigned long)(unsigned char)(ch0) | ((unsigned long)(unsigned char)(ch1) << 8) | \ + ((unsigned long)(unsigned char)(ch2) << 16) | ((unsigned long)(unsigned char)(ch3) << 24 )) + +/* + * FOURCC codes for DX compressed-texture pixel formats + */ +#define DDRAW_H_FOURCC_DXT1 (DDRAW_H_MAKEFOURCC('D','X','T','1')) +#define DDRAW_H_FOURCC_DXT2 (DDRAW_H_MAKEFOURCC('D','X','T','2')) +#define DDRAW_H_FOURCC_DXT3 (DDRAW_H_MAKEFOURCC('D','X','T','3')) +#define DDRAW_H_FOURCC_DXT4 (DDRAW_H_MAKEFOURCC('D','X','T','4')) +#define DDRAW_H_FOURCC_DXT5 (DDRAW_H_MAKEFOURCC('D','X','T','5')) + +/* + * DDSCAPS2 + */ +typedef struct _DDRAW_H_DDSCAPS2 +{ + unsigned long dwCaps; // capabilities of surface wanted + unsigned long dwCaps2; + unsigned long dwCaps3; + union + { + unsigned long dwCaps4; + unsigned long dwVolumeDepth; + } u1; +} DDRAW_H_DDSCAPS2; + +/* + * DDPIXELFORMAT + */ +typedef struct _DDRAW_H_DDPIXELFORMAT +{ + unsigned long dwSize; // size of structure + unsigned long dwFlags; // pixel format flags + unsigned long dwFourCC; // (FOURCC code) + union + { + unsigned long dwRGBBitCount; // how many bits per pixel + unsigned long dwYUVBitCount; // how many bits per pixel + unsigned long dwZBufferBitDepth; // how many total bits/pixel in z buffer (including any stencil bits) + unsigned long dwAlphaBitDepth; // how many bits for alpha channels + unsigned long dwLuminanceBitCount; // how many bits per pixel + unsigned long dwBumpBitCount; // how many bits per "buxel", total + unsigned long dwPrivateFormatBitCount; // Bits per pixel of private driver formats. Only valid in texture + // format list and if DDPF_D3DFORMAT is set + } u1; + union + { + unsigned long dwRBitMask; // mask for red bit + unsigned long dwYBitMask; // mask for Y bits + unsigned long dwStencilBitDepth; // how many stencil bits (note: dwZBufferBitDepth-dwStencilBitDepth is total Z-only bits) + unsigned long dwLuminanceBitMask; // mask for luminance bits + unsigned long dwBumpDuBitMask; // mask for bump map U delta bits + unsigned long dwOperations; // DDPF_D3DFORMAT Operations + } u2; + union + { + unsigned long dwGBitMask; // mask for green bits + unsigned long dwUBitMask; // mask for U bits + unsigned long dwZBitMask; // mask for Z bits + unsigned long dwBumpDvBitMask; // mask for bump map V delta bits + struct + { + unsigned short wFlipMSTypes; // Multisample methods supported via flip for this D3DFORMAT + unsigned short wBltMSTypes; // Multisample methods supported via blt for this D3DFORMAT + } MultiSampleCaps; + + } u3; + union + { + unsigned long dwBBitMask; // mask for blue bits + unsigned long dwVBitMask; // mask for V bits + unsigned long dwStencilBitMask; // mask for stencil bits + unsigned long dwBumpLuminanceBitMask; // mask for luminance in bump map + } u4; + union + { + unsigned long dwRGBAlphaBitMask; // mask for alpha channel + unsigned long dwYUVAlphaBitMask; // mask for alpha channel + unsigned long dwLuminanceAlphaBitMask; // mask for alpha channel + unsigned long dwRGBZBitMask; // mask for Z channel + unsigned long dwYUVZBitMask; // mask for Z channel + } u5; +} DDRAW_H_DDPIXELFORMAT; + +/* + * DDCOLORKEY + */ +typedef struct _DDRAW_H_DDCOLORKEY +{ + unsigned long dwColorSpaceLowValue; // low boundary of color space that is to + // be treated as Color Key, inclusive + unsigned long dwColorSpaceHighValue; // high boundary of color space that is +} DDRAW_H_DDCOLORKEY; + +/* + * DDSURFACEDESC2 + */ +typedef struct _DDRAW_H_DDSURFACEDESC2 +{ + unsigned long dwSize; // size of the DDSURFACEDESC structure + unsigned long dwFlags; // determines what fields are valid + unsigned long dwHeight; // height of surface to be created + unsigned long dwWidth; // width of input surface + union + { + long lPitch; // distance to start of next line (return value only) + unsigned long dwLinearSize; // Formless late-allocated optimized surface size + } u1; + union + { + unsigned long dwBackBufferCount; // number of back buffers requested + unsigned long dwDepth; // the depth if this is a volume texture + } u5; + union + { + unsigned long dwMipMapCount; // number of mip-map levels requestde + // dwZBufferBitDepth removed, use ddpfPixelFormat one instead + unsigned long dwRefreshRate; // refresh rate (used when display mode is described) + unsigned long dwSrcVBHandle; // The source used in VB::Optimize + } u2; + unsigned long dwAlphaBitDepth; // depth of alpha buffer requested + unsigned long dwReserved; // reserved + void *lpSurface; // pointer to the associated surface memory + union + { + DDRAW_H_DDCOLORKEY ddckCKDestOverlay; // color key for destination overlay use + unsigned long dwEmptyFaceColor; // Physical color for empty cubemap faces + } u3; + DDRAW_H_DDCOLORKEY ddckCKDestBlt; // color key for destination blt use + DDRAW_H_DDCOLORKEY ddckCKSrcOverlay; // color key for source overlay use + DDRAW_H_DDCOLORKEY ddckCKSrcBlt; // color key for source blt use + union + { + DDRAW_H_DDPIXELFORMAT ddpfPixelFormat; // pixel format description of the surface + unsigned long dwFVF; // vertex format description of vertex buffers + } u4; + DDRAW_H_DDSCAPS2 ddsCaps; // direct draw surface capabilities + unsigned long dwTextureStage; // stage in multitexture cascade +} DDRAW_H_DDSURFACEDESC2; + +typedef struct +{ + GLsizei width; + GLsizei height; + GLint components; + GLenum format; + + GLsizei cmapEntries; + GLenum cmapFormat; + GLubyte *cmap; + + GLubyte *pixels; +} GLGenericImage; + +GLGenericImage * ReadDDSFile(const char *filename, int * bufsize, int * numMipmaps) +{ + GLGenericImage * genericImage = NULL; + DDRAW_H_DDSURFACEDESC2 ddsd; + char filecode[4]; + FILE *fp = NULL; + int factor; + int result = false; + + /* try to open the file */ + fp = M_fopen(filename, "rb"); + if (fp != NULL) + { + if ((fread(filecode, 4, 1, fp) == 1) && + (strncmp(filecode, "DDS ", 4) == 0) && // verify the type of file + (fread(&ddsd, sizeof(ddsd), 1, fp) == 1)) // get the surface desc + { + genericImage = malloc(sizeof(GLGenericImage)); + if (genericImage) + { + memset(genericImage, 0, sizeof(GLGenericImage)); + + switch(ddsd.u4.ddpfPixelFormat.dwFourCC) + { + case DDRAW_H_FOURCC_DXT1: + genericImage->format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + factor = 2; + break; + case DDRAW_H_FOURCC_DXT3: + genericImage->format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + factor = 4; + break; + case DDRAW_H_FOURCC_DXT5: + genericImage->format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + factor = 4; + break; + default: + factor = -1; + break; + } + + if (factor != -1) + { + /* how big is it going to be including all mipmaps? */ + *bufsize = ddsd.u2.dwMipMapCount > 1 ? ddsd.u1.dwLinearSize * factor : ddsd.u1.dwLinearSize; + genericImage->pixels = malloc(*bufsize * sizeof(unsigned char)); + + if (fread(genericImage->pixels, 1, *bufsize, fp) > 0) + { + genericImage->width = ddsd.dwWidth; + genericImage->height = ddsd.dwHeight; + genericImage->components = (ddsd.u4.ddpfPixelFormat.dwFourCC == DDRAW_H_FOURCC_DXT1) ? 3 : 4; + *numMipmaps = ddsd.u2.dwMipMapCount; + + result = true; + } + } + } + } + + /* close the file pointer */ + fclose(fp); + } + + if (result) + { + /* return data */ + return genericImage; + } + else + { + if (genericImage) + free(genericImage); + + return NULL; + } +} + +static byte* RGB2PAL = NULL; + +static const char* gld_HiRes_GetInternalName(GLTexture *gltexture) +{ + static char texname[9]; + char *texname_p = NULL; + + switch (gltexture->textype) + { + case GLDT_TEXTURE: + texname_p = textures[gltexture->index]->name; + break; + case GLDT_FLAT: + case GLDT_PATCH: + texname_p = lumpinfo[gltexture->index].name; + break; + } + + if (!texname_p) + return NULL; + + strncpy(texname, texname_p, 8); + texname[8] = 0; + M_Strlwr(texname); + + return texname; +} + +static int gld_HiRes_GetExternalName(GLTexture *gltexture, char *img_path, char *dds_path) +{ + typedef struct hires_path_item_s + { + const char *path; + int exists; + } hires_path_item_t; + + typedef struct hires_path_s + { + const GameMission_t gamemission; + const GLTexType textype; + hires_path_item_t item[16]; + } hires_path_t; + + + static hires_path_t hires_paths[] = { + {doom, GLDT_TEXTURE, { + {"%stextures/doom/doom1/%s.%s", -1}, + {"%stextures/doom/doom1/%s-ck.%s", -1}, + {"%stextures/doom1/%s.%s", -1}, + {"%stextures/doom/%s.%s", -1}, + {"%stextures/doom/%s-ck.%s", -1}, + {"%stextures/%s.%s", -1}, + {"%stextures/%s-ck.%s", -1}, + {NULL, 0} + }}, + {doom, GLDT_FLAT, { + {"%sflats/doom/doom1/%s.%s", -1}, + {"%stextures/doom/doom1/flat-%s.%s", -1}, + {"%stextures/doom1/flat-%s.%s", -1}, + {"%sflats/doom/%s.%s", -1}, + {"%stextures/doom/flat-%s.%s", -1}, + {"%sflats/%s.%s", -1}, + {"%stextures/flat-%s.%s", -1}, + {NULL} + }}, + {doom, GLDT_PATCH, { + {"%spatches/doom/doom1/%s.%s", -1}, + {"%spatches/doom1-ultimate/%s.%s", -1}, + {"%spatches/doom/%s.%s", -1}, + {"%spatches/%s.%s", -1}, + {NULL} + }}, + + {doom2, GLDT_TEXTURE, { + {"%stextures/doom/doom2/%s.%s", -1}, + {"%stextures/doom/doom2/%s-ck.%s", -1}, + {"%stextures/doom/%s.%s", -1}, + {"%stextures/doom/%s-ck.%s", -1}, + {"%stextures/%s.%s", -1}, + {"%stextures/%s-ck.%s", -1}, + {NULL} + }}, + {doom2, GLDT_FLAT, { + {"%sflats/doom/doom2/%s.%s", -1}, + {"%stextures/doom/doom2/flat-%s.%s", -1}, + {"%sflats/doom/%s.%s", -1}, + {"%stextures/doom/flat-%s.%s", -1}, + {"%sflats/%s.%s", -1}, + {"%stextures/flat-%s.%s", -1}, + {NULL} + }}, + {doom2, GLDT_PATCH, { + {"%spatches/doom/doom2/%s.%s", -1}, + {"%spatches/doom2/%s.%s", -1}, + {"%spatches/doom/%s.%s", -1}, + {"%spatches/%s.%s", -1}, + {NULL} + }}, + + {pack_tnt, GLDT_TEXTURE, { + {"%stextures/doom/tnt/%s.%s", -1}, + {"%stextures/doom/tnt/%s-ck.%s", -1}, + {"%stextures/doom2-tnt/%s.%s", -1}, + {"%stextures/doom/doom2-tnt/%s.%s", -1}, + {"%stextures/doom/doom2-tnt/%s-ck.%s", -1}, + {"%stextures/doom/%s.%s", -1}, + {"%stextures/doom/%s-ck.%s", -1}, + {"%stextures/%s.%s", -1}, + {"%stextures/%s-ck.%s", -1}, + {NULL} + }}, + {pack_tnt, GLDT_FLAT, { + {"%sflats/doom/tnt/%s.%s", -1}, + {"%stextures/doom/tnt/flat-%s.%s", -1}, + {"%sflats/doom/doom2-tnt/%s.%s", -1}, + {"%stextures/doom/doom2-tnt/flat-%s.%s", -1}, + {"%stextures/doom2-tnt/flat-%s.%s", -1}, + {"%sflats/doom/%s.%s", -1}, + {"%stextures/doom/flat-%s.%s", -1}, + {"%sflats/%s.%s", -1}, + {"%stextures/flat-%s.%s", -1}, + {NULL} + }}, + {pack_tnt, GLDT_PATCH, { + {"%spatches/doom/tnt/%s.%s", -1}, + {"%spatches/doom2-tnt/%s.%s", -1}, + {"%spatches/tnt/%s.%s", -1}, + {"%spatches/doom/%s.%s", -1}, + {"%spatches/%s.%s", -1}, + {NULL} + }}, + + {pack_plut, GLDT_TEXTURE, { + {"%stextures/doom/plut/%s.%s", -1}, + {"%stextures/doom/plut/%s-ck.%s", -1}, + {"%stextures/doom2-plut/%s.%s", -1}, + {"%stextures/doom/doom2-plut/%s.%s", -1}, + {"%stextures/doom/doom2-plut/%s-ck.%s", -1}, + {"%stextures/doom/%s.%s", -1}, + {"%stextures/doom/%s-ck.%s", -1}, + {"%stextures/%s.%s", -1}, + {"%stextures/%s-ck.%s", -1}, + {NULL} + }}, + {pack_plut, GLDT_FLAT, { + {"%sflats/doom/plut/%s.%s", -1}, + {"%stextures/doom/plut/flat-%s.%s", -1}, + {"%sflats/doom/doom2-plut/%s.%s", -1}, + {"%stextures/doom/doom2-plut/flat-%s.%s", -1}, + {"%stextures/doom2-plut/flat-%s.%s", -1}, + {"%sflats/doom/%s.%s", -1}, + {"%stextures/doom/flat-%s.%s", -1}, + {"%sflats/%s.%s", -1}, + {"%stextures/flat-%s.%s", -1}, + {NULL} + }}, + {pack_plut, GLDT_PATCH, { + {"%spatches/doom/plut/%s.%s", -1}, + {"%spatches/doom2-plut/%s.%s", -1}, + {"%spatches/plutonia/%s.%s", -1}, + {"%spatches/doom/%s.%s", -1}, + {"%spatches/%s.%s", -1}, + {NULL} + }}, + + {hacx, GLDT_TEXTURE, { + {"%stextures/hacx/%s.%s", -1}, + {"%stextures/hacx/%s-ck.%s", -1}, + {"%stextures/%s.%s", -1}, + {"%stextures/%s-ck.%s", -1}, + {NULL} + }}, + {hacx, GLDT_FLAT, { + {"%sflats/hacx/%s.%s", -1}, + {"%stextures/hacx/flat-%s.%s", -1}, + {"%sflats/%s.%s", -1}, + {"%stextures/flat-%s.%s", -1}, + {NULL} + }}, + {hacx, GLDT_PATCH, { + {"%spatches/hacx/%s.%s", -1}, + {"%spatches/%s.%s", -1}, + {NULL} + }}, + + {chex, GLDT_TEXTURE, { + {"%stextures/chex/%s.%s", -1}, + {"%stextures/chex/%s-ck.%s", -1}, + {"%stextures/%s.%s", -1}, + {"%stextures/%s-ck.%s", -1}, + {NULL} + }}, + {chex, GLDT_FLAT, { + {"%sflats/chex/%s.%s", -1}, + {"%stextures/chex/flat-%s.%s", -1}, + {"%sflats/%s.%s", -1}, + {"%stextures/flat-%s.%s", -1}, + {NULL} + }}, + {chex, GLDT_PATCH, { + {"%spatches/chex/%s.%s", -1}, + {"%spatches/%s.%s", -1}, + {NULL} + }}, + + {none, GLDT_UNREGISTERED, { + {NULL} + }}, + }; + + static char *hiresdir = NULL; + + char texname[9]; + char *texname_p = NULL; + int i; + + hires_path_item_t *checklist = NULL; + GLTexType useType = gltexture->textype; + + dboolean supported = (gl_texture_external_hires && + ((useType == GLDT_TEXTURE) || (useType == GLDT_FLAT) || (useType == GLDT_PATCH))); + + img_path[0] = '\0'; + dds_path[0] = '\0'; + + if (!supported) + return false; + + i = 0; + while (hires_paths[i].gamemission != none) + { + if (gamemission == hires_paths[i].gamemission && + gltexture->textype == hires_paths[i].textype) + { + checklist = &hires_paths[i].item[0]; + break; + } + i++; + } + if (!checklist) + return false; + + switch (useType) + { + case GLDT_TEXTURE: + { + int i; + texture_t *texture = textures[gltexture->index]; + + if (!gl_hires_override_pwads) + { + for (i = 0; i < texture->patchcount; i++) + { + if (lumpinfo[texture->patches[i].patch].source != source_iwad) + return false; + } + } + texname_p = texture->name; + } + break; + case GLDT_FLAT: + case GLDT_PATCH: + { + if (!gl_hires_override_pwads) + { + if (lumpinfo[gltexture->index].source != source_iwad) + return false; + } + texname_p = lumpinfo[gltexture->index].name; + } + break; + } + + if (!texname_p) + return false; + + strncpy(texname, texname_p, 8); + texname[8] = 0; + M_Strlwr(texname); + + if (!hiresdir) + { + hiresdir = malloc(PATH_MAX); + if (strlen(gl_texture_hires_dir) > 0) + { + strncpy(hiresdir, gl_texture_hires_dir, PATH_MAX - 1); + } + else + { + strncpy(hiresdir, I_DoomExeDir(), PATH_MAX - 1); + } + // guarantee null delimiter + hiresdir[PATH_MAX - 1] = 0; + + if (!HasTrailingSlash(hiresdir)) + strcat(hiresdir, "/"); + } + + do + { + char checkName[PATH_MAX + 1]; + + if (checklist->exists == 0) + continue; + + if (checklist->exists == -1) + { + doom_snprintf(checkName, sizeof(checkName), checklist->path, hiresdir, "", ""); + if (!M_access(checkName, F_OK)) + checklist->exists = 1; + else + checklist->exists = 0; + } + + if (checklist->exists == 1) //dir exists + { + static const char * extensions[] = + {"png", "jpg", "tga", "pcx", "gif", "bmp", NULL}; + const char ** extp; + + if (GLEXT_glCompressedTexImage2DARB && dds_path[0] == '\0') + { + doom_snprintf(checkName, sizeof(checkName), checklist->path, hiresdir, texname, "dds"); + if (!M_access(checkName, F_OK)) + { + strcpy(dds_path, checkName); + } + } + + for (extp = extensions; *extp; extp++) + { + doom_snprintf(checkName, sizeof(checkName), checklist->path, hiresdir, texname, *extp); + + if (!M_access(checkName, F_OK)) + { + strcpy(img_path, checkName); + return true; + } + } + } + } + while ((++checklist)->path); + + if (dds_path[0]) + return true; + + return false; +} + +static void gld_HiRes_Bind(GLTexture *gltexture, GLuint *glTexID) +{ + switch (gltexture->textype) + { + case GLDT_TEXTURE: + gl_has_hires |= 1; + break; + case GLDT_FLAT: + gl_has_hires |= 2; + break; + case GLDT_PATCH: + gl_has_hires |= 4; + break; + } + + if ((gltexture->textype == GLDT_TEXTURE) || (gltexture->textype == GLDT_FLAT)) + gltexture->flags |= GLTEXTURE_MIPMAP; + else + gltexture->flags &= ~GLTEXTURE_MIPMAP; + + gltexture->flags |= GLTEXTURE_HIRES; + + if (gltexture->textype == GLDT_PATCH) + { + gltexture->scalexfac = 1.0f; + gltexture->scaleyfac = 1.0f; + } + + if (*glTexID == 0) + glGenTextures(1, glTexID); + + glBindTexture(GL_TEXTURE_2D, *glTexID); +} + +void gld_HiRes_ProcessColormap(unsigned char *buffer, int bufSize) +{ + int pos; + const lighttable_t *colormap; + const unsigned char *playpal; + + if (!RGB2PAL) + return; + + playpal = V_GetPlaypal(); + colormap = (fixedcolormap ? fixedcolormap : fullcolormap); + + for (pos = 0; pos < bufSize; pos += 4) + { +#if 1 + byte color; + + if (gl_hires_24bit_colormap) + color = RGB2PAL[(buffer[pos+0]<<16) + (buffer[pos+1]<<8) + buffer[pos+2]]; + else + color = RGB2PAL[((buffer[pos+0]>>3)<<10) + ((buffer[pos+1]>>3)<<5) + (buffer[pos+2]>>3)]; + + buffer[pos+0] = playpal[colormap[color]*3+0]; + buffer[pos+1] = playpal[colormap[color]*3+1]; + buffer[pos+2] = playpal[colormap[color]*3+2]; +#endif + +#if 0 + float factor; + int c, r, g, b, m; + byte color; + + color = RGB2PAL[(buffer[pos+0]<<16) + (buffer[pos+1]<<8) + buffer[pos+2]]; + + factor = + 0.30f * playpal[color*3+0] + + 0.59f * playpal[color*3+1] + + 0.11f * playpal[color*3+2]; + + if (fabs(factor) < 0.001f) + factor = 1; + else + factor = (0.3f * buffer[pos+0] + 0.59f * buffer[pos+1] + 0.11f * buffer[pos+2]) / factor; + + r = (int)(playpal[colormap[color]*3+0] * factor); + g = (int)(playpal[colormap[color]*3+1] * factor); + b = (int)(playpal[colormap[color]*3+2] * factor); + + m = 255; + if (r > m) + { + m = r; + factor = 255.0f / (float)playpal[colormap[color]*3+0]; + } + if (g > m) + { + m = g; + factor = 255.0f / (float)playpal[colormap[color]*3+1]; + } + if (b > m) + { + m = b; + factor = 255.0f / (float)playpal[colormap[color]*3+2]; + } + + c = (int)(playpal[colormap[color]*3+0] * factor); + buffer[pos+0] = BETWEEN(0, 255, c); + c = (int)(playpal[colormap[color]*3+1] * factor); + buffer[pos+1] = BETWEEN(0, 255, c); + c = (int)(playpal[colormap[color]*3+2] * factor); + buffer[pos+2] = BETWEEN(0, 255, c); +#endif + +#if 0 + float factor; + int c; + + color = RGB2PAL[(buffer[pos+0]<<16) + (buffer[pos+1]<<8) + buffer[pos+2]]; + + factor = + 0.30f * playpal[color*3+0] + + 0.59f * playpal[color*3+1] + + 0.11f * playpal[color*3+2]; + + if (fabs(factor) < 0.001f) + factor = 1; + else + factor = (0.3f * buffer[pos+0] + 0.59f * buffer[pos+1] + 0.11f * buffer[pos+2]) / factor; + + c = (int)(playpal[colormap[color]*3+0] * factor); + buffer[pos+0] = BETWEEN(0, 255, c); + c = (int)(playpal[colormap[color]*3+1] * factor); + buffer[pos+1] = BETWEEN(0, 255, c); + c = (int)(playpal[colormap[color]*3+2] * factor); + buffer[pos+2] = BETWEEN(0, 255, c); +#endif + } +} + +int gld_HiRes_BuildTables(void) +{ +#define RGB2PAL_NAME "RGB2PAL" + const int chanel_bits = (gl_hires_24bit_colormap ? 8 : 5); + const int numcolors_per_chanel = (1 << chanel_bits); + const int RGB2PAL_size = numcolors_per_chanel * numcolors_per_chanel * numcolors_per_chanel; + unsigned char* RGB2PAL_fname; + int lump, size; + + if ((!gl_boom_colormaps) || !(gl_texture_internal_hires || gl_texture_external_hires)) + return false; + + if (RGB2PAL) + return true; + + if (gl_hires_24bit_colormap) + { + lump = W_CheckNumForName(RGB2PAL_NAME); + if (lump != -1) + { + size = W_LumpLength(lump); + if (size == RGB2PAL_size) + { + const byte* RGB2PAL_lump; + + RGB2PAL_lump = W_CacheLumpNum(lump); + RGB2PAL = malloc(RGB2PAL_size); + memcpy(RGB2PAL, RGB2PAL_lump, RGB2PAL_size); + W_UnlockLumpName(RGB2PAL_NAME); + return true; + } + } + + RGB2PAL_fname = I_FindFile(RGB2PAL_NAME".dat", ".dat"); + if (RGB2PAL_fname) + { + struct stat RGB24to8_stat; + memset(&RGB24to8_stat, 0, sizeof(RGB24to8_stat)); + M_stat(RGB2PAL_fname, &RGB24to8_stat); + size = 0; + if (RGB24to8_stat.st_size == RGB2PAL_size) + { + I_FileToBuffer(RGB2PAL_fname, &RGB2PAL, &size); + } + free(RGB2PAL_fname); + + if (size == RGB2PAL_size) + return true; + } + } + + if (1 || M_CheckParm("-"RGB2PAL_NAME)) + { + int ok = true; + FILE *RGB2PAL_fp = NULL; + char fname[PATH_MAX+1]; + + if (gl_hires_24bit_colormap) + { + doom_snprintf(fname, sizeof(fname), "%s/"RGB2PAL_NAME".dat", I_DoomExeDir()); + RGB2PAL_fp = M_fopen(fname, "wb"); + ok = RGB2PAL_fp != NULL; + } + + if (ok) + { + void* NewIntDynArray(int dimCount, int *dims); + const byte* palette; + int r, g, b, k, color; + int **x, **y, **z; + int dims[2] = {numcolors_per_chanel, 256}; + + x = NewIntDynArray(2, dims); + y = NewIntDynArray(2, dims); + z = NewIntDynArray(2, dims); + + RGB2PAL = malloc(RGB2PAL_size); + palette = V_GetPlaypal(); + + // create the RGB24to8 lookup table + gld_ProgressStart(); + gld_ProgressUpdate(NULL, 0, numcolors_per_chanel); + for (k = 0; k < numcolors_per_chanel; k++) + { + int color_p = 0; + int kk = (gl_hires_24bit_colormap ? k : (k<<3)|(k>>2)); + for (color = 0; color < 256; color++) + { + x[k][color] = (kk - palette[color_p++]); + x[k][color] *= x[k][color]; + y[k][color] = (kk - palette[color_p++]); + y[k][color] *= y[k][color]; + z[k][color] = (kk - palette[color_p++]); + z[k][color] *= z[k][color]; + } + } + + k = 0; + for (r = 0; r < numcolors_per_chanel; r++) + { + gld_ProgressUpdate(NULL, r, numcolors_per_chanel); + for (g = 0; g < numcolors_per_chanel; g++) + { + int xy[256]; + for (color = 0; color < 256; color++) + { + xy[color] = x[r][color] + y[g][color]; + } + for (b = 0; b < numcolors_per_chanel; b++) + { + int dist; + int bestcolor = 0; + int bestdist = xy[0] + z[b][0]; + #define CHECK_BEST dist = xy[color] + z[b][color];\ + if (dist < bestdist) {bestdist = dist; bestcolor = color;} color++; + for (color = 0; color < 256;) + { + CHECK_BEST; + CHECK_BEST; + CHECK_BEST; + CHECK_BEST; + CHECK_BEST; + CHECK_BEST; + CHECK_BEST; + CHECK_BEST; + } + RGB2PAL[k++] = bestcolor; + } + } + } + gld_ProgressEnd(); + + free(z); + free(y); + free(x); + + if (gl_hires_24bit_colormap) + { + ok = fwrite(RGB2PAL, RGB2PAL_size, 1, RGB2PAL_fp) == 1; + return ((fclose(RGB2PAL_fp) == 0) && ok); + } + else + { + return true; + } + } + } + + gl_boom_colormaps_default = false; + M_ChangeAllowBoomColormaps(); + return false; +} + +void gld_InitHiRes(void) +{ + gld_HiRes_BuildTables(); + + gl_has_hires = 0; + + gld_PrecacheGUIPatches(); +} + +static int gld_HiRes_LoadDDSTexture(GLTexture* gltexture, GLuint* texid, const char* dds_path) +{ + int result = false; + int tex_width, tex_height; + + if (GLEXT_glCompressedTexImage2DARB) + { + int ddsbufsize, numMipmaps; + GLGenericImage * ddsimage = ReadDDSFile(dds_path, &ddsbufsize, &numMipmaps); + + if (ddsimage) + { + tex_width = gld_GetTexDimension(ddsimage->width); + tex_height = gld_GetTexDimension(ddsimage->height); + + if (tex_width == ddsimage->width && tex_height == ddsimage->height) + { + int i, offset, size, blockSize; + + gld_HiRes_Bind(gltexture, texid); + + if (numMipmaps > 1) + gltexture->flags |= GLTEXTURE_MIPMAP; + else + gltexture->flags &= ~GLTEXTURE_MIPMAP; + + offset = 0; + blockSize = (ddsimage->format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16; + + /* load the mipmaps */ + for (i = 0; i < (numMipmaps ? numMipmaps : 1) && (ddsimage->width || ddsimage->height); i++) + { + if (ddsimage->width == 0) + ddsimage->width = 1; + if (ddsimage->height == 0) + ddsimage->height = 1; + + size = ((ddsimage->width + 3) / 4) * ((ddsimage->height + 3) / 4) * blockSize; + + GLEXT_glCompressedTexImage2DARB(GL_TEXTURE_2D, i, ddsimage->format, + ddsimage->width, ddsimage->height, + 0, size, ddsimage->pixels + offset); + + // GLErrorReport(); + offset += size; + ddsimage->width >>= 1; + ddsimage->height >>= 1; + } + + gld_SetTexFilters(gltexture); + + free(ddsimage->pixels); + free(ddsimage); + + result = true; + } + } + } + + return result; +} + +static int gld_HiRes_LoadFromCache(GLTexture* gltexture, GLuint* texid, const char* img_path) +{ + int result = false; + int tex_width = 0; + int tex_height = 0; + int tex_buffer_size; + struct stat cache_stat; + struct stat tex_stat; + char* cache_filename; + FILE *cachefp; + unsigned char *tex_buffer; + + memset(&tex_stat, 0, sizeof(tex_stat)); + M_stat(img_path, &tex_stat); + + cache_filename = malloc(strlen(img_path) + 16); + sprintf(cache_filename, "%s.cache", img_path); + + cachefp = M_fopen(cache_filename, "rb"); + if (cachefp) + { + if (fread(&tex_width, sizeof(tex_width), 1, cachefp) == 1 && + fread(&tex_height, sizeof(tex_height), 1, cachefp) == 1 && + fread(&cache_stat.st_mtime, sizeof(cache_stat.st_mtime), 1, cachefp) == 1) + { + if (cache_stat.st_mtime == tex_stat.st_mtime) + { + tex_buffer_size = tex_width * tex_height * 4; + + tex_buffer = malloc(tex_buffer_size); + if (tex_buffer) + { + if (fread(tex_buffer, tex_buffer_size, 1, cachefp) == 1) + { + gld_HiRes_Bind(gltexture, texid); + gld_BuildTexture(gltexture, tex_buffer, false, tex_width, tex_height); + + result = true; + } + } + } + } + fclose(cachefp); + } + + free(cache_filename); + + return result; +} + +static int gld_HiRes_WriteCache(GLTexture* gltexture, GLuint* texid, const char* img_path) +{ + int result = false; + int w, h; + unsigned char *buf; + unsigned char cache_filename[PATH_MAX]; + struct stat tex_stat; + FILE *cachefp; + + doom_snprintf(cache_filename, sizeof(cache_filename), "%s.cache", img_path); + if (M_access(cache_filename, F_OK)) + { + buf = gld_GetTextureBuffer(*texid, 0, &w, &h); + if (buf) + { + memset(&tex_stat, 0, sizeof(tex_stat)); + M_stat(img_path, &tex_stat); + cachefp = M_fopen(cache_filename, "wb"); + if (cachefp) + { + result = + (fwrite(&w, sizeof(w), 1, cachefp) == 1) && + (fwrite(&h, sizeof(h), 1, cachefp) == 1) && + (fwrite(&tex_stat.st_mtime, sizeof(tex_stat.st_mtime), 1, cachefp) == 1) && + (fwrite(buf, w * h * 4, 1, cachefp) == 1); + + fclose(cachefp); + } + } + } + + if (!result) + { + lprintf(LO_WARN, "gld_HiRes_WriteCache: error writing '%s'.\n", cache_filename); + } + + return result; +} + +static int gld_HiRes_LoadFromFile(GLTexture* gltexture, GLuint* texid, const char* img_path) +{ + int result = false; + SDL_Surface *surf = NULL; + SDL_Surface *surf_tmp = NULL; + + surf_tmp = IMG_Load(img_path); + + if (!surf_tmp) + { + lprintf(LO_WARN, "gld_HiRes_LoadExternal: %s\n", SDL_GetError()); + } + else + { + surf = SDL_ConvertSurface(surf_tmp, &RGBAFormat, 0); + SDL_FreeSurface(surf_tmp); + + if (surf) + { + if (SDL_LockSurface(surf) >= 0) + { + if (SmoothEdges(surf->pixels, surf->pitch / 4, surf->h)) + gltexture->flags |= GLTEXTURE_HASHOLES; + else + gltexture->flags &= ~GLTEXTURE_HASHOLES; + SDL_UnlockSurface(surf); + } + gld_HiRes_Bind(gltexture, texid); + result = gld_BuildTexture(gltexture, surf->pixels, true, surf->w, surf->h); + + SDL_FreeSurface(surf); + } + } + + return result; +} + +int gld_LoadHiresTex(GLTexture *gltexture, int cm) +{ + int result = false; + GLuint *texid; + + // do we need it? + if ((gl_texture_external_hires || gl_texture_internal_hires) && + !(gltexture->flags & GLTEXTURE_HASNOHIRES)) + { + // default buffer + texid = &gltexture->glTexExID[CR_DEFAULT][0][0]; + + // do not try to load hires twice + if ((*texid == 0) || (gltexture->flags & GLTEXTURE_HIRES)) + { + // try to load in-wad texture + if (*texid == 0 && gl_texture_internal_hires) + { + const char *lumpname = gld_HiRes_GetInternalName(gltexture); + + if (lumpname) + { + int lump = (W_CheckNumForName)(lumpname, ns_hires); + if (lump != -1) + { + SDL_RWops *rw_data = SDL_RWFromConstMem(W_CacheLumpNum(lump), W_LumpLength(lump)); + SDL_Surface *surf_tmp = IMG_Load_RW(rw_data, false); + + // SDL can't load some TGA with common method + if (!surf_tmp) + { + surf_tmp = IMG_LoadTyped_RW(rw_data, false, "TGA"); + } + + SDL_FreeRW(rw_data); + + if (!surf_tmp) + { + lprintf(LO_WARN, "gld_LoadHiresTex: %s\n", SDL_GetError()); + } + else + { + SDL_Surface *surf = SDL_ConvertSurface(surf_tmp, &RGBAFormat, 0); + SDL_FreeSurface(surf_tmp); + + if (surf) + { + if (SDL_LockSurface(surf) >= 0) + { + if (SmoothEdges(surf->pixels, surf->pitch / 4, surf->h)) + gltexture->flags |= GLTEXTURE_HASHOLES; + else + gltexture->flags &= ~GLTEXTURE_HASHOLES; + SDL_UnlockSurface(surf); + } + gld_HiRes_Bind(gltexture, texid); + gld_BuildTexture(gltexture, surf->pixels, true, surf->w, surf->h); + + SDL_FreeSurface(surf); + } + } + } + } + } + + // then external + if (*texid == 0 && gl_texture_external_hires) + { + char img_path[PATH_MAX]; + char dds_path[PATH_MAX]; + if (gld_HiRes_GetExternalName(gltexture, img_path, dds_path)) + { + if (!gld_HiRes_LoadDDSTexture(gltexture, texid, dds_path)) + { + if (!gld_HiRes_LoadFromCache(gltexture, texid, img_path)) + { + if (gld_HiRes_LoadFromFile(gltexture, texid, img_path)) + { + if ((gltexture->realtexwidth != gltexture->tex_width) || + (gltexture->realtexheight != gltexture->tex_height)) + { + gld_HiRes_WriteCache(gltexture, texid, img_path); + } + } + } + } + } + } + + if (*texid) + { + gld_GetTextureTexID(gltexture, cm); + + if (last_glTexID == gltexture->texid_p) + { + result = true; + } + else + { + if (texid == gltexture->texid_p) + { + glBindTexture(GL_TEXTURE_2D, *gltexture->texid_p); + result = true; + } + else + { + //if (gl_boom_colormaps && use_boom_cm && + // !(comp[comp_skymap] && (gltexture->flags&GLTEXTURE_SKY))) + if (boom_cm && use_boom_cm && gl_boom_colormaps) + { + int w, h; + unsigned char *buf; + + if (*gltexture->texid_p == 0) + { + buf = gld_GetTextureBuffer(*texid, 0, &w, &h); + gld_HiRes_Bind(gltexture, gltexture->texid_p); + gld_HiRes_ProcessColormap(buf, w * h * 4); + if (gld_BuildTexture(gltexture, buf, true, w, h)) + { + result = true; + } + } + else + { + gld_HiRes_Bind(gltexture, gltexture->texid_p); + result = true; + } + } + else + { + *gltexture->texid_p = *texid; + gld_HiRes_Bind(gltexture, gltexture->texid_p); + result = true; + } + } + } + } + } + } + + if (result) + { + last_glTexID = gltexture->texid_p; + } + else + { + // there is no corresponding hires + gltexture->flags |= GLTEXTURE_HASNOHIRES; + } + + return result; +} + +int gld_PrecacheGUIPatches(void) +{ + static const char * staticpatches[] = { + "INTERPIC",// "TITLEPIC", + + //doom's M_* + "M_DETAIL", "M_DISOPT", "M_DISP", "M_DOOM", + "M_ENDGAM", "M_EPI1", "M_EPI2", "M_EPI3", + "M_EPISOD", "M_GDHIGH", "M_GDLOW", "M_HURT", + "M_JKILL", "M_LGTTL", "M_LOADG", "M_LSCNTR", + "M_LSLEFT", "M_LSRGHT", "M_MESSG", "M_MSENS", + "M_MSGOFF", "M_MSGON", "M_MUSVOL", "M_NEWG", + "M_NGAME", "M_NMARE", "M_OPTION", "M_OPTTTL", + "M_PAUSE", "M_QUITG", "M_RDTHIS", "M_ROUGH", + "M_SAVEG", "M_SCRNSZ", "M_SFXVOL", "M_SGTTL", + "M_SKILL", "M_SKULL1", "M_SKULL2", "M_SVOL", + "M_THERML", "M_THERMM", "M_THERMO", "M_THERMR", + "M_ULTRA", + "M_EPI4", + + //prboom's M_* + "M_ACCEL", "M_AUTO", "M_BUTT1", "M_BUTT2", + "M_CHAT", "M_COLORS", "M_COMPAT", "M_COMPAT", + "M_ENEM", "M_GENERL", "M_HORSEN", "M_KEYBND", + "M_KEYBND", "M_LOKSEN", "M_MESS", "M_PALNO", + "M_PALSEL", "M_SETUP", "M_STAT", "M_STAT", + "M_VBOX", "M_VERSEN", "M_WEAP", + + NULL + }; + + const char ** patch_p; + int count, total; + + if (!gl_texture_external_hires) + return 0; + + gld_ProgressStart(); + + count = 0; total = 0; + for (patch_p = staticpatches; *patch_p; patch_p++) + total++; + + for (patch_p = staticpatches; *patch_p; patch_p++) + { + int lump = W_CheckNumForName(*patch_p); + if (lump > 0) + { + GLTexture *gltexture; + + lumpinfo[lump].flags |= LUMP_STATIC; + gltexture = gld_RegisterPatch(lump, CR_DEFAULT, false); + if (gltexture) + { + gld_BindPatch(gltexture, CR_DEFAULT); + if (gltexture && (gltexture->flags & GLTEXTURE_HIRES)) + { + gld_ProgressUpdate("Loading GUI Patches...", ++count, total); + } + } + } + } + + gld_ProgressEnd(); + + return 0; +} + +#endif // HAVE_LIBSDL2_IMAGE diff --git a/src/gl_hqresize.c b/src/gl_hqresize.c new file mode 100644 index 0000000..2a8aed8 --- /dev/null +++ b/src/gl_hqresize.c @@ -0,0 +1,288 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +/* +** gl_hqresize.cpp +** Contains high quality upsampling functions. +** So far Scale2x/3x/4x as described in http://scale2x.sourceforge.net/ +** are implemented. +** +**--------------------------------------------------------------------------- +** Copyright 2008 Benjamin Berkels +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "doomstat.h" +#include "v_video.h" +#include "gl_intern.h" + +int gl_texture_hqresize; +const char *gl_hqresizemodes[hq_scale_max] = { + "Off", "Scale2x", "Scale3x", "Scale4x"}; + +int gl_texture_hqresize; +int gl_texture_hqresize_maxinputsize = 512; +int gl_texture_hqresize_textures; +int gl_texture_hqresize_sprites; +int gl_texture_hqresize_patches; + +static void scale2x ( unsigned int* inputBuffer, unsigned int* outputBuffer, int inWidth, int inHeight, int seamlessWidth, int seamlessHeight ) +{ + int i, j; + const int width = 2* inWidth; + //const int height = 2 * inHeight; + + for ( i = 0; i < inWidth; ++i ) + { + // [JB] when the current index is at an edge and seamlessWidth is true, + // the opposite edge's index will be used for iMinus and iPlus + // when the current index is at an edge and seamlessWidth is false, + // the current index will be used for iMinus and iPlus + // otherwise iMinus and iPlus are equal to i-1 and i+1 respectively + const int iMinus = ((i == 0) ? (seamlessWidth ? inWidth - 1 : 0) : (i - 1)); + const int iPlus = ((i == inWidth - 1) ? (seamlessWidth ? 0 : i) : (i + 1)); + for ( j = 0; j < inHeight; ++j ) + { + const int jMinus = ((j == 0) ? (seamlessHeight ? inHeight - 1 : 0) : (j - 1)); + const int jPlus = ((j == inHeight - 1) ? (seamlessHeight ? 0 : j) : (j + 1)); + //const unsigned int A = inputBuffer[ iMinus +inWidth*jMinus]; + const unsigned int B = inputBuffer[ iMinus +inWidth*j ]; + //const unsigned int C = inputBuffer[ iMinus +inWidth*jPlus]; + const unsigned int D = inputBuffer[ i +inWidth*jMinus]; + const unsigned int E = inputBuffer[ i +inWidth*j ]; + const unsigned int F = inputBuffer[ i +inWidth*jPlus]; + //const unsigned int G = inputBuffer[ iPlus +inWidth*jMinus]; + const unsigned int H = inputBuffer[ iPlus +inWidth*j ]; + //const unsigned int I = inputBuffer[ iPlus +inWidth*jPlus]; + if (B != H && D != F) { + outputBuffer[2*i + width*2*j ] = D == B ? D : E; + outputBuffer[2*i + width*(2*j+1)] = B == F ? F : E; + outputBuffer[2*i+1 + width*2*j ] = D == H ? D : E; + outputBuffer[2*i+1 + width*(2*j+1)] = H == F ? F : E; + } else { + outputBuffer[2*i + width*2*j ] = E; + outputBuffer[2*i + width*(2*j+1)] = E; + outputBuffer[2*i+1 + width*2*j ] = E; + outputBuffer[2*i+1 + width*(2*j+1)] = E; + } + } + } +} + +static void scale3x ( unsigned int* inputBuffer, unsigned int* outputBuffer, int inWidth, int inHeight, int seamlessWidth, int seamlessHeight ) +{ + int i, j; + const int width = 3* inWidth; + //const int height = 3 * inHeight; + + for ( i = 0; i < inWidth; ++i ) + { + const int iMinus = ((i == 0) ? (seamlessWidth ? inWidth - 1 : 0) : (i - 1)); + const int iPlus = ((i == inWidth - 1) ? (seamlessWidth ? 0 : i) : (i + 1)); + for ( j = 0; j < inHeight; ++j ) + { + const int jMinus = ((j == 0) ? (seamlessHeight ? inHeight - 1 : 0) : (j - 1)); + const int jPlus = ((j == inHeight - 1) ? (seamlessHeight ? 0 : j) : (j + 1)); + const unsigned int A = inputBuffer[ iMinus +inWidth*jMinus]; + const unsigned int B = inputBuffer[ iMinus +inWidth*j ]; + const unsigned int C = inputBuffer[ iMinus +inWidth*jPlus]; + const unsigned int D = inputBuffer[ i +inWidth*jMinus]; + const unsigned int E = inputBuffer[ i +inWidth*j ]; + const unsigned int F = inputBuffer[ i +inWidth*jPlus]; + const unsigned int G = inputBuffer[ iPlus +inWidth*jMinus]; + const unsigned int H = inputBuffer[ iPlus +inWidth*j ]; + const unsigned int I = inputBuffer[ iPlus +inWidth*jPlus]; + if (B != H && D != F) { + outputBuffer[3*i + width*3*j ] = D == B ? D : E; + outputBuffer[3*i + width*(3*j+1)] = (D == B && E != C) || (B == F && E != A) ? B : E; + outputBuffer[3*i + width*(3*j+2)] = B == F ? F : E; + outputBuffer[3*i+1 + width*3*j ] = (D == B && E != G) || (D == H && E != A) ? D : E; + outputBuffer[3*i+1 + width*(3*j+1)] = E; + outputBuffer[3*i+1 + width*(3*j+2)] = (B == F && E != I) || (H == F && E != C) ? F : E; + outputBuffer[3*i+2 + width*3*j ] = D == H ? D : E; + outputBuffer[3*i+2 + width*(3*j+1)] = (D == H && E != I) || (H == F && E != G) ? H : E; + outputBuffer[3*i+2 + width*(3*j+2)] = H == F ? F : E; + } else { + outputBuffer[3*i + width*3*j ] = E; + outputBuffer[3*i + width*(3*j+1)] = E; + outputBuffer[3*i + width*(3*j+2)] = E; + outputBuffer[3*i+1 + width*3*j ] = E; + outputBuffer[3*i+1 + width*(3*j+1)] = E; + outputBuffer[3*i+1 + width*(3*j+2)] = E; + outputBuffer[3*i+2 + width*3*j ] = E; + outputBuffer[3*i+2 + width*(3*j+1)] = E; + outputBuffer[3*i+2 + width*(3*j+2)] = E; + } + } + } +} + +static void scale4x ( unsigned int* inputBuffer, unsigned int* outputBuffer, int inWidth, int inHeight, int seamlessWidth, int seamlessHeight ) +{ + unsigned int * buffer2x = malloc((2 * inWidth) * (2 * inHeight) * sizeof(unsigned int)); + scale2x (inputBuffer, buffer2x, inWidth, inHeight, seamlessWidth, seamlessHeight); + scale2x (buffer2x, outputBuffer, 2 * inWidth, 2 * inHeight, seamlessWidth, seamlessHeight); + free(buffer2x); +} + + +static unsigned char *HQScaleHelper( void (*scaleNxFunction) ( unsigned int* , unsigned int* , int , int, int, int), + const int N, + unsigned char *inputBuffer, + const int inWidth, + const int inHeight, + int *outWidth, + int *outHeight, + int seamlessWidth, + int seamlessHeight ) +{ + unsigned char * newBuffer; + + (*outWidth) = N * inWidth; + (*outHeight) = N *inHeight; + newBuffer = malloc((*outWidth) * (*outHeight) * 4 * sizeof(unsigned char)); + + scaleNxFunction ( (unsigned int*)inputBuffer, (unsigned int*)newBuffer, inWidth, inHeight, seamlessWidth, seamlessHeight ); + free(inputBuffer); + inputBuffer = NULL; + return newBuffer; +} + +//=========================================================================== +// +// [BB] Upsamples the texture in inputBuffer, frees inputBuffer and returns +// the upsampled buffer. +// +//=========================================================================== +unsigned char* gld_HQResize(GLTexture *gltexture, unsigned char *inputBuffer, int inWidth, int inHeight, int *outWidth, int *outHeight) +{ + int w = inWidth; + int h = inHeight; + unsigned char *result = inputBuffer; + gl_hqresizemode_t scale_mode = hq_scale_none; + int sw = 0; + int sh = 0; + + if (outWidth) *outWidth = inWidth; + if (outHeight) *outHeight = inHeight; + + if (!gl_texture_hqresize || !gltexture || !inputBuffer) + return result; + + // [BB] Don't resample if the width or height of the input texture is bigger than gl_texture_hqresize_maxinputsize. + if ((inWidth > gl_texture_hqresize_maxinputsize) || + (inHeight > gl_texture_hqresize_maxinputsize)) + return result; + + // [BB] The hqnx upsampling (not the scaleN one) destroys partial transparency, don't upsamle textures using it. + //if (gltexture->bIsTransparent == 1) + // return inputBuffer; + + switch (gltexture->textype) + { + case GLDT_PATCH: + sw = sh = 0; + if (gltexture->flags & GLTEXTURE_SPRITE) + scale_mode = gl_texture_hqresize_sprites; + else + scale_mode = gl_texture_hqresize_patches; + break; + + case GLDT_FLAT: + sw = sh = 1; + scale_mode = gl_texture_hqresize_textures; + break; + + case GLDT_TEXTURE: + //sw = gltexture->flags & GLTEXTURE_SKY; + //sh = 0; + sw = sh = 1; + scale_mode = gl_texture_hqresize_textures; + break; + } + + switch (scale_mode) + { + case hq_scale_2x: + result = HQScaleHelper(&scale2x, 2, inputBuffer, inWidth, inHeight, &w, &h, sw, sh); + break; + case hq_scale_3x: + result = HQScaleHelper(&scale3x, 3, inputBuffer, inWidth, inHeight, &w, &h, sw, sh); + break; + case hq_scale_4x: + result = HQScaleHelper(&scale4x, 4, inputBuffer, inWidth, inHeight, &w, &h, sw, sh); + break; + } + + if (result != inputBuffer) + { + gltexture->tex_width = w; + gltexture->tex_height = h; + + if (outWidth) *outWidth = w; + if (outHeight) *outHeight = h; + } + + return result; +} diff --git a/src/gl_intern.h b/src/gl_intern.h new file mode 100644 index 0000000..b4be8d5 --- /dev/null +++ b/src/gl_intern.h @@ -0,0 +1,589 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifndef _GL_INTERN_H +#define _GL_INTERN_H + +#include "v_video.h" +#include "xs_Float.h" + +#define MAXCOORD (32767.0f / MAP_COEFF) + +#define SMALLDELTA 0.001f + +typedef enum +{ + GLDT_UNREGISTERED, + GLDT_BROKEN, + GLDT_PATCH, + GLDT_TEXTURE, + GLDT_FLAT +} GLTexType; + +typedef enum +{ + MIP_TEXTURE, + MIP_SPRITE, + MIP_PATCH, + + MIP_COUNT +} GLMipType; + +typedef struct tex_filter_s +{ + int mipmap; + int mag_filter; + int min_filter; +} tex_filter_t; + +typedef enum +{ + GLTEXTURE_SPRITE = 0x00000002, + GLTEXTURE_HASHOLES = 0x00000004, + GLTEXTURE_SKY = 0x00000008, + GLTEXTURE_HIRES = 0x00000010, + GLTEXTURE_HASNOHIRES= 0x00000020, + GLTEXTURE_CLAMPX = 0x00000040, + GLTEXTURE_CLAMPY = 0x00000080, + GLTEXTURE_CLAMPXY = (GLTEXTURE_CLAMPX | GLTEXTURE_CLAMPY), + GLTEXTURE_MIPMAP = 0x00000100, +} GLTexture_flag_t; + +typedef struct gl_strip_coords_s +{ + GLfloat v[4][3]; + + GLfloat t[4][2]; +} gl_strip_coords_t; + +#define PLAYERCOLORMAP_COUNT (3) + +typedef struct detail_s +{ + GLuint texid; + int texture_num; + float width, height; + float offsetx, offsety; +} detail_t; + +typedef struct +{ + int index; + int width,height; + int leftoffset,topoffset; + int tex_width,tex_height; + int realtexwidth, realtexheight; + int buffer_width,buffer_height; + int buffer_size; + + //e6y: support for Boom colormaps + GLuint ***glTexExID; + unsigned int texflags[CR_LIMIT+MAXPLAYERS][PLAYERCOLORMAP_COUNT]; + GLuint *texid_p; + unsigned int *texflags_p; + + int cm; + int player_cm; + + GLTexType textype; + unsigned int flags; + float scalexfac, scaleyfac; //e6y: right/bottom UV coordinates for patch drawing + + //detail + detail_t *detail; + float detail_width, detail_height; +} GLTexture; + +typedef struct +{ + float x1,x2; + float z1,z2; + dboolean fracleft, fracright; //e6y +} GLSeg; + +typedef struct +{ + GLSeg *glseg; + float ytop,ybottom; + float ul,ur,vt,vb; + float light; + float fogdensity; + float alpha; + float skyymid; + float skyyaw; + GLTexture *gltexture; + byte flag; + seg_t *seg; +} GLWall; + +typedef enum +{ + GLFLAT_CEILING = 0x00000001, + GLFLAT_HAVE_OFFSET = 0x00000002, +} GLFlat_flag_t; + +typedef struct +{ + int sectornum; + float light; // the lightlevel of the flat + float fogdensity; + float uoffs,voffs; // the texture coordinates + float z; // the z position of the flat (height) + GLTexture *gltexture; + unsigned int flags; + float alpha; +} GLFlat; + +/* GLLoopDef is the struct for one loop. A loop is a list of vertexes + * for triangles, which is calculated by the gluTesselator in gld_PrecalculateSector + * and in gld_PreprocessCarvedFlat + */ +typedef struct +{ + int index; // subsector index + GLenum mode; // GL_TRIANGLES, GL_TRIANGLE_STRIP or GL_TRIANGLE_FAN + int vertexcount; // number of vertexes in this loop + int vertexindex; // index into vertex list +} GLLoopDef; + +// GLSector is the struct for a sector with a list of loops. + +#define SECTOR_CLAMPXY 0x00000001 +typedef struct +{ + int loopcount; // number of loops for this sector + GLLoopDef *loops; // the loops itself + unsigned int flags; +} GLSector; + +typedef struct +{ + int loopcount; // number of loops for this sector + GLLoopDef *loops; // the loops itself +} GLMapSubsector; + +typedef struct +{ + GLfloat x; + GLfloat y; + GLfloat z; +} GLVertex; + +typedef struct +{ + GLfloat u; + GLfloat v; +} GLTexcoord; + +typedef struct +{ + GLLoopDef loop; // the loops itself +} GLSubSector; + +typedef struct +{ + float x, y, z; + float radius; + float light; +} GLShadow; + +typedef struct +{ + int cm; + + float x1, x2, x3; + float z1, z2, z3; + float y; +} GLHealthBar; + +extern GLSeg *gl_segs; +extern GLSeg *gl_lines; + +#define GLDWF_TOP 1 +#define GLDWF_M1S 2 +#define GLDWF_M2S 3 +#define GLDWF_BOT 4 +#define GLDWF_TOPFLUD 5 //e6y: project the ceiling plane into the gap +#define GLDWF_BOTFLUD 6 //e6y: project the floor plane into the gap +#define GLDWF_SKY 7 +#define GLDWF_SKYFLIP 8 + +typedef struct +{ + int cm; + float x,y,z; + float vt,vb; + float ul,ur; + float x1,y1; + float x2,y2; + float light; + float fogdensity; + fixed_t scale; + GLTexture *gltexture; + uint_64_t flags; + int index; + int xy; + fixed_t fx,fy; +} GLSprite; + +typedef enum +{ + GLDIT_NONE, + + GLDIT_WALL, // opaque wall + GLDIT_MWALL, // opaque mid wall + GLDIT_FWALL, // projected wall + GLDIT_TWALL, // transparent walls + GLDIT_SWALL, // sky walls + + GLDIT_AWALL, // animated wall + GLDIT_FAWALL, // animated projected wall + + GLDIT_CEILING, // ceiling + GLDIT_FLOOR, // floor + + GLDIT_ACEILING, // animated ceiling + GLDIT_AFLOOR, // animated floor + + GLDIT_SPRITE, // opaque sprite + GLDIT_TSPRITE, // transparent sprites + GLDIT_ASPRITE, + + GLDIT_SHADOW, + + GLDIT_HBAR, + + GLDIT_TYPES +} GLDrawItemType; + +typedef struct GLDrawItem_s +{ + union + { + void *item; + GLWall *wall; + GLFlat *flat; + GLSprite *sprite; + GLShadow *shadow; + GLHealthBar *hbar; + } item; +} GLDrawItem; + +typedef struct GLDrawDataItem_s +{ + byte *data; + int maxsize; + int size; +} GLDrawDataItem_t; + +typedef struct +{ + GLDrawDataItem_t *data; + int maxsize; + int size; + + GLDrawItem *items[GLDIT_TYPES]; + int num_items[GLDIT_TYPES]; + int max_items[GLDIT_TYPES]; +} GLDrawInfo; + +void gld_AddDrawItem(GLDrawItemType itemtype, void *itemdata); + +void gld_DrawTriangleStrip(GLWall *wall, gl_strip_coords_t *c); +void gld_DrawTriangleStripARB(GLWall *wall, gl_strip_coords_t *c1, gl_strip_coords_t *c2); + +extern float roll; +extern float yaw; +extern float inv_yaw; +extern float pitch; + +extern int gl_compatibility; +extern int gl_ztrick; +extern int gl_finish; + +extern int gl_preprocessed; //e6y + +extern GLDrawInfo gld_drawinfo; +void gld_FreeDrawInfo(void); +void gld_ResetDrawInfo(void); + +extern GLSector *sectorloops; +extern GLMapSubsector *subsectorloops; + +extern const char *gl_tex_format_string; +extern int gl_tex_format; +extern int gl_texture_filter_anisotropic; +extern int transparent_pal_index; +extern unsigned char gld_palmap[256]; +extern tex_filter_t tex_filter[]; +void gld_SetTexFilters(GLTexture *gltexture); + +extern float xCamera,yCamera,zCamera; + +// +//detail +// + +int gld_IsDetailVisible(float x0, float y0, float x1, float y1, float x2, float y2); +void gld_InitDetail(void); +void gld_InitFrameDetails(void); +void gld_ParseDetail(void); +void gld_SetTexDetail(GLTexture *gltexture); + +void gld_PreprocessDetail(void); +void gld_DrawDetail_NoARB(void); +void gld_EnableDetail(int enable); +void gld_DrawWallWithDetail(GLWall *wall); +void gld_BindDetail(GLTexture *gltexture, int enable); +void gld_BindDetailARB(GLTexture *gltexture, int enable); +void gld_DrawItemsSortByDetail(GLDrawItemType itemtype); +void gld_DrawWallDetail_NoARB(GLWall *wall); + +extern int render_usedetail; +extern int scene_has_details; +extern detail_t *details; +extern int details_count; + +extern int scene_has_wall_details; +extern int scene_has_flat_details; + +extern GLuint* last_glTexID; +GLTexture *gld_RegisterTexture(int texture_num, dboolean mipmap, dboolean force); +void gld_BindTexture(GLTexture *gltexture, unsigned int flags); +GLTexture *gld_RegisterPatch(int lump, int cm, dboolean is_sprite); +void gld_BindPatch(GLTexture *gltexture, int cm); +GLTexture *gld_RegisterFlat(int lump, dboolean mipmap); +void gld_BindFlat(GLTexture *gltexture, unsigned int flags); +void gld_InitPalettedTextures(void); +int gld_GetTexDimension(int value); +void gld_SetTexturePalette(GLenum target); +void gld_Precache(void); + +void SetFrameTextureMode(void); + +//gamma +void gld_ResetGammaRamp(void); + +//gl_vertex +void gld_SplitLeftEdge(const GLWall *wall, dboolean detail); +void gld_SplitRightEdge(const GLWall *wall, dboolean detail); +void gld_RecalcVertexHeights(const vertex_t *v); + +//e6y +void gld_InitGLVersion(void); +void gld_ResetLastTexture(void); + +unsigned char* gld_GetTextureBuffer(GLuint texid, int miplevel, int *width, int *height); + +int gld_BuildTexture(GLTexture *gltexture, void *data, dboolean readonly, int width, int height); + +//hires +extern unsigned int gl_has_hires; +int gld_HiRes_BuildTables(void); +void gld_InitHiRes(void); +int gld_LoadHiresTex(GLTexture *gltexture, int cm); +void gld_GetTextureTexID(GLTexture *gltexture, int cm); +GLuint CaptureScreenAsTexID(void); +void gld_ProgressUpdate(const char * text, int progress, int total); +int gld_ProgressStart(void); +int gld_ProgressEnd(void); + +//FBO +#define INVUL_CM 0x00000001 +#define INVUL_INV 0x00000002 +#define INVUL_BW 0x00000004 +extern GLuint glSceneImageFBOTexID; +extern GLuint glSceneImageTextureFBOTexID; + +extern unsigned int invul_method; +extern float bw_red; +extern float bw_green; +extern float bw_blue; +extern int SceneInTexture; +void gld_InitFBO(void); +void gld_FreeScreenSizeFBO(void); + +//motion bloor +extern int gl_motionblur; + +extern int imageformats[]; + +//missing flats (fake floors and ceilings) + +void gld_PreprocessFakeSectors(void); + +void gld_SetupFloodStencil(GLWall *wall); +void gld_ClearFloodStencil(GLWall *wall); + +void gld_SetupFloodedPlaneCoords(GLWall *wall, gl_strip_coords_t *c); +void gld_SetupFloodedPlaneLight(GLWall *wall); + +//light +extern int gl_rellight; +void gld_StaticLightAlpha(float light, float alpha); +#define gld_StaticLight(light) gld_StaticLightAlpha(light, 1.0f) +void gld_InitLightTable(void); +typedef float (*gld_CalcLightLevel_f)(int lightlevel); +typedef float (*gld_Calc2DLightLevel_f)(int lightlevel); +extern gld_CalcLightLevel_f gld_CalcLightLevel; +extern gld_Calc2DLightLevel_f gld_Calc2DLightLevel; + +//fog +extern int gl_fog; +extern int gl_use_fog; +void gl_EnableFog(int on); +void gld_SetFog(float fogdensity); +typedef float (*gld_CalcFogDensity_f)(sector_t *sector, int lightlevel, GLDrawItemType type); +extern gld_CalcFogDensity_f gld_CalcFogDensity; + +//HQ resize +unsigned char* gld_HQResize(GLTexture *gltexture, unsigned char *inputBuffer, int inWidth, int inHeight, int *outWidth, int *outHeight); + +// SkyBox +#define SKY_NONE 0 +#define SKY_CEILING 1 +#define SKY_FLOOR 2 +typedef struct PalEntry_s +{ + unsigned char r, g, b; +} PalEntry_t; +typedef struct SkyBoxParams_s +{ + int index; + unsigned int type; + GLWall wall; + float x_scale, y_scale; + float x_offset, y_offset; + // 0 - no colormap; 1 - INVUL inverse colormap + PalEntry_t FloorSkyColor[2]; + PalEntry_t CeilingSkyColor[2]; + // for BoxSkybox + side_t *side; +} SkyBoxParams_t; +extern SkyBoxParams_t SkyBox; +extern GLfloat gl_whitecolor[]; +void gld_InitSky(void); +void gld_AddSkyTexture(GLWall *wall, int sky1, int sky2, int skytype); +void gld_GetSkyCapColors(void); +void gld_InitFrameSky(void); +void gld_DrawStripsSky(void); +void gld_DrawScreenSkybox(void); +void gld_GetScreenSkyScale(GLWall *wall, float *scale_x, float *scale_y); +void gld_DrawDomeSkyBox(void); +void gld_DrawSkyCaps(void); +int gld_DrawBoxSkyBox(void); + +// shadows +void gld_InitShadows(void); +void gld_ProcessThingShadow(mobj_t *mo); +void gld_RenderShadows(void); + +// VBO +typedef struct vbo_vertex_s +{ + float x, y, z; + float u, v; + unsigned char r, g, b, a; +} PACKEDATTR vbo_vertex_t; +#define NULL_VBO_VERTEX ((vbo_vertex_t*)NULL) +#define sky_vbo_x (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_VERTEX->x : &vbo->data[0].x) +#define sky_vbo_u (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_VERTEX->u : &vbo->data[0].u) +#define sky_vbo_r (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_VERTEX->r : &vbo->data[0].r) + +typedef struct vbo_xyz_uv_s +{ + float x, y, z; + float u, v; +} PACKEDATTR vbo_xyz_uv_t; +extern vbo_xyz_uv_t *flats_vbo; +#define NULL_VBO_XYZ_UV ((vbo_xyz_uv_t*)NULL) +#define flats_vbo_x (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_XYZ_UV->x : &flats_vbo[0].x) +#define flats_vbo_u (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_XYZ_UV->u : &flats_vbo[0].u) + +typedef struct vbo_xy_uv_rgba_s +{ + float x, y; + float u, v; + unsigned char r, g, b, a; +} PACKEDATTR vbo_xy_uv_rgba_t; + +//BoxSkybox +typedef struct box_skybox_s +{ + char name[9]; + int fliptop; + char faces[6][9]; + GLTexture texture[6]; +} box_skybox_t; +box_skybox_t* R_GetBoxSkybox(int index); +void gld_ParseSkybox(void); +extern box_skybox_t *BoxSkybox_default; + +// display lists +void gld_InitDisplayLists(void); +void gld_CleanDisplayLists(void); +extern int flats_display_list; + +// preprocessing +extern byte *segrendered; // true if sector rendered (only here for malloc) +extern byte *linerendered[2]; // true if linedef rendered (only here for malloc) +extern GLuint flats_vbo_id; + +#ifdef USE_SHADERS + +typedef struct GLShader_s +{ + char name[256]; + GLhandleARB hShader; + GLhandleARB hVertProg; + GLhandleARB hFragProg; + + int lightlevel_index; +} GLShader; + +extern GLShader *sh_main; + +int glsl_Init(void); +void glsl_SetActiveShader(GLShader *shader); +void glsl_SetLightLevel(float lightlevel); +int glsl_IsActive(void); + +#else + +#define glsl_Init() 0 +#define glsl_SetActiveShader(shader) +#define glsl_SetLightLevel(lightlevel) + +#endif // USE_SHADERS + +#endif // _GL_INTERN_H diff --git a/src/gl_light.c b/src/gl_light.c new file mode 100644 index 0000000..78b66c5 --- /dev/null +++ b/src/gl_light.c @@ -0,0 +1,449 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#include "doomstat.h" +#include "lprintf.h" +#include "v_video.h" +#include "r_main.h" +#include "gl_intern.h" +#include "gl_opengl.h" +#include "e6y.h" + +gl_lightmode_t gl_lightmode; +gl_lightmode_t gl_lightmode_default; +const char *gl_lightmodes[] = {"glboom", "gzdoom", "fog based", "shaders"}; +int gl_light_ambient; +int gl_rellight; + +int gl_fog; +int gl_use_fog; +int gl_fog_color; + +int gl_fogenabled; +int gl_distfog = 70; +float gl_CurrentFogDensity = -1.0f; +float distfogtable[3][256]; + +typedef void (*gld_InitLightTable_f)(void); + +typedef struct +{ + int use_hwgamma; + int rellight; + gld_InitLightTable_f Init; + gld_CalcLightLevel_f GetLight; + gld_Calc2DLightLevel_f Get2DLight; + gld_CalcFogDensity_f GetFog; +} GLLight; + +static float lighttable_glboom[5][256]; +static float lighttable_gzdoom[256]; +static float lighttable_fogbased[256]; + +static void gld_InitLightTable_glboom(void); +static void gld_InitLightTable_gzdoom(void); +static void gld_InitLightTable_fogbased(void); + +static float gld_CalcLightLevel_glboom(int lightlevel); +static float gld_CalcLightLevel_gzdoom(int lightlevel); +static float gld_CalcLightLevel_fogbased(int lightlevel); +static float gld_CalcLightLevel_shaders(int lightlevel); + +static float gld_CalcFogDensity_glboom(sector_t *sector, int lightlevel, GLDrawItemType type); +static float gld_CalcFogDensity_gzdoom(sector_t *sector, int lightlevel, GLDrawItemType type); +static float gld_CalcFogDensity_fogbased(sector_t *sector, int lightlevel, GLDrawItemType type); + +static GLLight gld_light[gl_lightmode_last] = { + //gl_lightmode_glboom + {false, 16, + gld_InitLightTable_glboom, + gld_CalcLightLevel_glboom, gld_CalcLightLevel_glboom, + gld_CalcFogDensity_glboom}, + + //gl_lightmode_gzdoom + {true, 8, + gld_InitLightTable_gzdoom, + gld_CalcLightLevel_gzdoom, gld_CalcLightLevel_gzdoom, + gld_CalcFogDensity_gzdoom}, + + //gl_lightmode_fogbased + {true, 8, + gld_InitLightTable_fogbased, + gld_CalcLightLevel_fogbased, gld_CalcLightLevel_gzdoom, + gld_CalcFogDensity_fogbased}, + + //gl_lightmode_shaders + {true, 16, + gld_InitLightTable_glboom, + gld_CalcLightLevel_shaders, gld_CalcLightLevel_glboom, + gld_CalcFogDensity_glboom}, +}; + +int gl_hardware_gamma = false; +gld_CalcLightLevel_f gld_CalcLightLevel = gld_CalcLightLevel_glboom; +gld_Calc2DLightLevel_f gld_Calc2DLightLevel = gld_CalcLightLevel_glboom; +gld_CalcFogDensity_f gld_CalcFogDensity = gld_CalcFogDensity_glboom; + +void M_ChangeLightMode(void) +{ + if (gl_compatibility) + { + if (gl_lightmode_default == gl_lightmode_fogbased || + gl_lightmode_default == gl_lightmode_shaders) + { + lprintf(LO_INFO, + "M_ChangeLightMode: '%s' sector light mode is not allowed in gl_compatibility mode\n", + gl_lightmodes[gl_lightmode_default]); + gl_lightmode_default = gl_lightmode_glboom; + } + } + + if (gl_lightmode_default == gl_lightmode_shaders) + { + if (!glsl_Init()) + { + gl_lightmode_default = gl_lightmode_glboom; + } + } + + gl_lightmode = gl_lightmode_default; + + gl_hardware_gamma = gld_light[gl_lightmode].use_hwgamma; + gl_rellight = gld_light[gl_lightmode].rellight; + gld_CalcLightLevel = gld_light[gl_lightmode].GetLight; + gld_Calc2DLightLevel = gld_light[gl_lightmode].Get2DLight; + gld_CalcFogDensity = gld_light[gl_lightmode].GetFog; + + if (gld_light[gl_lightmode].use_hwgamma) + { + gld_SetGammaRamp(useglgamma); + } + else + { + gld_SetGammaRamp(-1); + gld_FlushTextures(); + } +} + +void gld_InitLightTable(void) +{ + int i; + + for (i = 0; i < gl_lightmode_last; i++) + { + gld_light[i].Init(); + } +} + +/* + * lookuptable for lightvalues + * calculated as follow: + * floatlight=(1.0-exp((light^3)*gamma)) / (1.0-exp(1.0*gamma)); + * gamma=-0,2;-2,0;-4,0;-6,0;-8,0 + * light=0,0 .. 1,0 + */ +static void gld_InitLightTable_glboom(void) +{ + int i, g; + float gamma[5] = {-0.2f, -2.0f, -4.0f, -6.0f, -8.0f}; + + for (g = 0; g < 5; g++) + { + for (i = 0; i < 256; i++) + { + lighttable_glboom[g][i] = (float)((1.0f - exp(pow(i / 255.0f, 3) * gamma[g])) / (1.0f - exp(1.0f * gamma[g]))); + } + } +} + +static void gld_InitLightTable_gzdoom(void) +{ + int i; + float light; + + for (i = 0; i < 256; i++) + { + if (i < 192) + light = (192.0f - (192 - i) * 1.95f); + else + light = (float)i; + + if (light < gl_light_ambient) + light = (float)gl_light_ambient; + + lighttable_gzdoom[i] = light / 255.0f; + } + + lighttable_gzdoom[0] = 0.0f; +} + +static void gld_InitLightTable_fogbased(void) +{ + int i; + float light; + + for (i = 0; i < 256; i++) + { + if (i < 192) + light = (float)255; + else + light = (float)i; + + lighttable_fogbased[i] = light / 255.0f; + } +} + +static float gld_CalcLightLevel_glboom(int lightlevel) +{ + return lighttable_glboom[usegamma][BETWEEN(0, 255, lightlevel)]; +} + +static float gld_CalcLightLevel_gzdoom(int lightlevel) +{ + return lighttable_gzdoom[BETWEEN(0, 255, lightlevel)]; +} + +static float gld_CalcLightLevel_fogbased(int lightlevel) +{ + if (players[displayplayer].fixedcolormap) + return lighttable_gzdoom[BETWEEN(0, 255, lightlevel)]; + else + { + if (extralight) + return lighttable_fogbased[255]; + else + return lighttable_fogbased[BETWEEN(0, 255, lightlevel)]; + } +} + +static float gld_CalcLightLevel_shaders(int lightlevel) +{ + int light; + + light = BETWEEN(0, 255, lightlevel); + + return (float)light/255.0f; +} + +void gld_StaticLightAlpha(float light, float alpha) +{ + player_t *player = &players[displayplayer]; + int shaders = (gl_lightmode == gl_lightmode_shaders); + + if (!player->fixedcolormap) + { + float ll = (shaders ? 1.0f : light); + glColor4f(ll, ll, ll, alpha); + } + else + { + if (!(invul_method & INVUL_BW)) + { + glColor4f(1.0f, 1.0f, 1.0f, alpha); + } + else + { +#ifdef USE_FBO_TECHNIQUE + if (SceneInTexture) + { + glColor4f(0.5f, 0.5f, 0.5f, alpha); + } + else +#endif + { + glColor4f(bw_red, bw_green, bw_blue, alpha); + } + } + } + + if (shaders) + { + glsl_SetLightLevel((player->fixedcolormap ? 1.0f : light)); + } +} + +void M_ChangeAllowFog(void) +{ + int i; + GLfloat FogColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + + FogColor[0] = ((float)((gl_fog_color >> 16) & 0xff)) / 255.0f; + FogColor[1] = ((float)((gl_fog_color >> 8) & 0xff)) / 255.0f; + FogColor[2] = ((float)((gl_fog_color >> 0) & 0xff)) / 255.0f; + FogColor[3] = 0.0f; + + glFogi (GL_FOG_MODE, GL_EXP); + glFogfv(GL_FOG_COLOR, FogColor); + glHint (GL_FOG_HINT, GL_NICEST); + + gl_CurrentFogDensity = -1; + + gl_EnableFog(true); + gl_EnableFog(false); + + for (i = 0; i < 256; i++) + { + if (i < 164) + { + distfogtable[0][i] = (float)((gl_distfog >> 1) + (gl_distfog) * (164 - i) / 164); + } + else if (i < 230) + { + distfogtable[0][i] = (float)((gl_distfog >> 1) - (gl_distfog >> 1) * (i - 164) / (230 - 164)); + } + else + { + distfogtable[0][i] = 0.0f; + } + + if (i < 128) + { + distfogtable[1][i] = 6.0f + (gl_distfog >> 1) + (gl_distfog) * (128 - i) / 48; + } + else if (i < 216) + { + distfogtable[1][i] = (216.0f - i) / ((216.0f - 128.0f)) * gl_distfog / 10; + } + else + { + distfogtable[1][i] = 0.0f; + } + + if (i <= 128) + { + distfogtable[2][i] = (float)(1<<16) / (float)pow(1.46f, ((float)i / 8.0f)); + if (distfogtable[2][i] > 2048) + distfogtable[2][i] = 2048; + } + else if (i < 192) + { + distfogtable[2][i] = (float)(1<<13) / (float)pow(1.30f, ((float)i / 8.0f)); + } + else if (i < 216) + { + distfogtable[2][i] = (216.0f - i) / ((216.0f - 128.0f)) * gl_distfog / 10; + } + else + { + distfogtable[2][i] = 0.0f; + } + } +} + +static float gld_CalcFogDensity_glboom(sector_t *sector, int lightlevel, GLDrawItemType type) +{ + return 0; +} + +static float gld_CalcFogDensity_gzdoom(sector_t *sector, int lightlevel, GLDrawItemType type) +{ + if ((!gl_use_fog) || + (sector && (sector->ceilingpic == skyflatnum || sector->floorpic == skyflatnum))) + { + return 0; + } + else + { + return distfogtable[1][BETWEEN(0, 255, lightlevel)]; + } +} + +static float gld_CalcFogDensity_fogbased(sector_t *sector, int lightlevel, GLDrawItemType type) +{ + if (players[displayplayer].fixedcolormap) + { + return 0; + } + else + { + float fog = distfogtable[2][BETWEEN(0, 255, lightlevel)]; + + if (extralight) + { + if (extralight == 1) + fog -= fog / 3.0f; + else + fog -= fog / 2.0f; + } + + if (type == GLDIT_CEILING || type == GLDIT_FLOOR || type == GLDIT_FWALL) + { + return fog * 2; + } + else + { + return fog; + } + } +} + +void gld_SetFog(float fogdensity) +{ + if (fogdensity) + { + gl_EnableFog(true); + if (fogdensity != gl_CurrentFogDensity) + { + glFogf(GL_FOG_DENSITY, fogdensity / 512.0f); + gl_CurrentFogDensity = fogdensity; + } + } + else + { + gl_EnableFog(false); + gl_CurrentFogDensity = -1.0f; + } +} + +void gl_EnableFog(int on) +{ + if (on) + { + if (!gl_fogenabled) + glEnable(GL_FOG); + } + else + { + if (gl_fogenabled) + glDisable(GL_FOG); + } + gl_fogenabled=on; +} diff --git a/src/gl_main.c b/src/gl_main.c new file mode 100644 index 0000000..943e664 --- /dev/null +++ b/src/gl_main.c @@ -0,0 +1,3360 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * Copyright 2007 by + * Andrey Budko, Roman Marchenko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gl_opengl.h" + +#include "z_zone.h" +#include +#include +#include "doomtype.h" +#include "w_wad.h" +#include "m_argv.h" +#include "d_event.h" +#include "v_video.h" +#include "doomstat.h" +#include "r_bsp.h" +#include "r_main.h" +#include "r_draw.h" +#include "r_sky.h" +#include "r_plane.h" +#include "r_data.h" +#include "r_things.h" +#include "r_fps.h" +#include "p_maputl.h" +#include "m_bbox.h" +#include "lprintf.h" +#include "gl_intern.h" +#include "gl_struct.h" +#include "p_spec.h" +#include "i_system.h" +#include "m_argv.h" +#include "i_video.h" +#include "i_main.h" +#include "am_map.h" +#include "sc_man.h" +#include "st_stuff.h" +#include "hu_stuff.h" +#include "e6y.h"//e6y + +// All OpenGL extentions will be disabled in gl_compatibility mode +int gl_compatibility = 0; + +int gl_clear; + +int gl_preprocessed = false; + +int gl_spriteindex; +int scene_has_overlapped_sprites; + +int gl_blend_animations; + +int gl_use_display_lists; +int flats_display_list; +int flats_display_list_size = 0; +int flats_detail_display_list; +int flats_detail_display_list_size = 0; + +int gl_finish = 1; + +// e6y +// This variables toggles the use of a trick to prevent the clearning of the +// z-buffer between frames. When this variable is set to "1", the game will not +// clear the z-buffer between frames. This will result in increased performance +// only on very old hardware and might cause problems for some display hardware. +int gl_ztrick; +int gl_ztrickframe = 0; +float gldepthmin, gldepthmax; + +unsigned int invul_method; +float bw_red = 0.3f; +float bw_green = 0.59f; +float bw_blue = 0.11f; + +extern int tran_filter_pct; + +dboolean use_fog=false; + +int gl_nearclip=5; +int gl_texture_filter; +int gl_sprite_filter; +int gl_patch_filter; +int gl_texture_filter_anisotropic = 0; + +//sprites +spriteclipmode_t gl_spriteclip; +const char *gl_spriteclipmodes[] = {"constant", "full", "smart"}; +int gl_spriteclip_threshold; +float gl_spriteclip_threshold_f; +int gl_sprites_frustum_culling; +int gl_sprite_offset_default; // item out of floor offset Mead 8/13/03 +float gl_sprite_offset; // precalcilated float value for gl_sprite_offset_default +int gl_sprite_blend; // e6y: smooth sprite edges +int gl_mask_sprite_threshold; +float gl_mask_sprite_threshold_f; +/* Note: These must be identically-indexed, + * including the spritefuzzmode enum */ +spritefuzzmode_t gl_thingspritefuzzmode; +spritefuzzmode_t gl_weaponspritefuzzmode; +const char *gl_spritefuzzmodes[] = {"darken", "shadow", "transparent", "ghostly"}; +GLenum gl_fuzzsfactors[] = {GL_DST_COLOR, GL_ZERO, GL_ONE, GL_SRC_ALPHA}; +GLenum gl_fuzzdfactors[] = {GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, GL_ONE}; + +GLuint gld_DisplayList=0; +int fog_density=200; +static float extra_red=0.0f; +static float extra_green=0.0f; +static float extra_blue=0.0f; +static float extra_alpha=0.0f; + +GLfloat gl_whitecolor[4]={1.0f,1.0f,1.0f,1.0f}; + +GLfloat cm2RGB[CR_LIMIT + 1][4] = +{ + {0.50f ,0.00f, 0.00f, 1.00f}, //CR_BRICK + {1.00f ,1.00f, 1.00f, 1.00f}, //CR_TAN + {1.00f ,1.00f, 1.00f, 1.00f}, //CR_GRAY + {0.00f ,1.00f, 0.00f, 1.00f}, //CR_GREEN + {0.50f ,0.20f, 1.00f, 1.00f}, //CR_BROWN + {1.00f ,1.00f, 0.00f, 1.00f}, //CR_GOLD + {1.00f ,0.00f, 0.00f, 1.00f}, //CR_RED + {0.80f ,0.80f, 1.00f, 1.00f}, //CR_BLUE + {1.00f ,0.50f, 0.25f, 1.00f}, //CR_ORANGE + {1.00f ,1.00f, 0.00f, 1.00f}, //CR_YELLOW + {0.50f ,0.50f, 1.00f, 1.00f}, //CR_BLUE2 + {0.00f ,0.00f, 0.00f, 1.00f}, //CR_BLACK + {0.50f ,0.00f, 0.50f, 1.00f}, //CR_PURPLE + {1.00f ,1.00f, 1.00f, 1.00f}, //CR_WHITE + {1.00f ,1.00f, 1.00f, 1.00f}, //CR_LIMIT +}; + + +void SetFrameTextureMode(void) +{ +#ifdef USE_FBO_TECHNIQUE + if (SceneInTexture) + { + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); + } + else +#endif + if (invul_method & INVUL_BW) + { + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_DOT3_RGB); + glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_RGB,GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_RGB,GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE1_RGB,GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_RGB,GL_SRC_COLOR); + } + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); +} + +void gld_InitTextureParams(void) +{ + typedef struct tex_filter_s + { + dboolean mipmap; + int tex_filter; + int mipmap_filter; + const char *tex_filter_name; + const char *mipmap_filter_name; + } tex_filter_t; + + typedef struct tex_format_s + { + int tex_format; + const char *tex_format_name; + } tex_format_t; + + tex_filter_t params[filter_count] = { + {false, GL_NEAREST, GL_NEAREST, "GL_NEAREST", "GL_NEAREST"}, + {false, GL_LINEAR, GL_LINEAR, "GL_LINEAR", "GL_LINEAR"}, + {true, GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, "GL_NEAREST", "GL_NEAREST_MIPMAP_NEAREST"}, + {true, GL_NEAREST, GL_NEAREST_MIPMAP_LINEAR, "GL_NEAREST", "GL_NEAREST_MIPMAP_LINEAR"}, + {true, GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST, "GL_LINEAR", "GL_LINEAR_MIPMAP_NEAREST"}, + {true, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, "GL_LINEAR", "GL_LINEAR_MIPMAP_LINEAR"}, + }; + + tex_format_t tex_formats[] = { + {GL_RGBA2, "GL_RGBA2"}, + {GL_RGBA4, "GL_RGBA4"}, + {GL_RGB5_A1, "GL_RGB5_A1"}, + {GL_RGBA8, "GL_RGBA8"}, + {GL_RGBA, "GL_RGBA"}, + {0, NULL} + }; + + int i; + int *var[MIP_COUNT] = {&gl_texture_filter, &gl_sprite_filter, &gl_patch_filter}; + + for (i = 0; i < MIP_COUNT; i++) + { +#ifdef USE_GLU_MIPMAP + tex_filter[i].mipmap = params[*var[i]].mipmap; +#else + tex_filter[i].mipmap = false; +#endif + tex_filter[i].mag_filter = params[*var[i]].tex_filter; + tex_filter[i].min_filter = params[*var[i]].mipmap_filter; + } + + if (tex_filter[MIP_TEXTURE].mipmap) + { + gl_shared_texture_palette = false; + } + + i = 0; + while (tex_formats[i].tex_format_name) + { + if (!strcasecmp(gl_tex_format_string, tex_formats[i].tex_format_name)) + { + gl_tex_format = tex_formats[i].tex_format; + lprintf(LO_INFO,"Using texture format %s.\n", tex_formats[i].tex_format_name); + break; + } + i++; + } +} + +void gld_MultisamplingInit(void) +{ + if (render_multisampling) + { + extern int gl_colorbuffer_bits; + extern int gl_depthbuffer_bits; + + gl_colorbuffer_bits = 32; + SDL_GL_SetAttribute( SDL_GL_BUFFER_SIZE, gl_colorbuffer_bits ); + + if (gl_depthbuffer_bits!=8 && gl_depthbuffer_bits!=16 && gl_depthbuffer_bits!=24) + gl_depthbuffer_bits = 16; + SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, gl_depthbuffer_bits ); + + SDL_GL_SetAttribute ( SDL_GL_MULTISAMPLESAMPLES, render_multisampling ); + SDL_GL_SetAttribute ( SDL_GL_MULTISAMPLEBUFFERS, 1 ); + } +} + +void gld_MultisamplingCheck(void) +{ + if (render_multisampling) + { + int test = -1; + SDL_GL_GetAttribute (SDL_GL_MULTISAMPLESAMPLES, &test); + if (test!=render_multisampling) + { + void M_SaveDefaults (void); + int i=render_multisampling; + render_multisampling = 0; + M_SaveDefaults (); + I_Error("Couldn't set %dX multisamples for %dx%d video mode", i, SCREENWIDTH, SCREENHEIGHT); + } + } +} + +void gld_MultisamplingSet(void) +{ + if (render_multisampling) + { + int use_multisampling = map_use_multisamling || + (!(automapmode & am_active) || (automapmode & am_overlay)); + + gld_EnableMultisample(use_multisampling); + } +} + +int gld_LoadGLDefs(const char * defsLump) +{ + typedef enum + { + TAG_SKYBOX, + TAG_DETAIL, + + TAG_MAX + } gldef_type_e; + + // these are the core types available in the *DEFS lump + static const char *CoreKeywords[TAG_MAX + 1] = + { + "skybox", + "detail", + + NULL + }; + + int result = false; + + if (W_CheckNumForName(defsLump) != -1) + { + SC_OpenLump(defsLump); + + // Get actor class name. + while (SC_GetString()) + { + switch (SC_MatchString(CoreKeywords)) + { + case TAG_SKYBOX: + result = true; + gld_ParseSkybox(); + break; + case TAG_DETAIL: + result = true; + gld_ParseDetail(); + break; + } + } + + SC_Close(); + } + + return result; +} + + + +void gld_Init(int width, int height) +{ + GLfloat params[4]={0.0f,0.0f,1.0f,0.0f}; + + lprintf(LO_INFO,"GL_VENDOR: %s\n",glGetString(GL_VENDOR)); + lprintf(LO_INFO,"GL_RENDERER: %s\n",glGetString(GL_RENDERER)); + lprintf(LO_INFO,"GL_VERSION: %s\n",glGetString(GL_VERSION)); + lprintf(LO_INFO,"GL_EXTENSIONS:\n"); + { + char ext_name[256]; + const char *extensions = (const char*)glGetString(GL_EXTENSIONS); + const char *rover = extensions; + const char *p = rover; + + while (*rover) + { + size_t len = 0; + p = rover; + while (*p && *p != ' ') + { + p++; + len++; + } + if (*p) + { + len = MIN(len, sizeof(ext_name)-1); + memset(ext_name, 0, sizeof(ext_name)); + strncpy(ext_name, rover, len); + lprintf(LO_INFO,"\t%s\n", ext_name); + } + rover = p; + while (*rover && *rover == ' ') + rover++; + } + } + + gld_InitOpenGL(gl_compatibility); + gld_InitPalettedTextures(); + gld_InitTextureParams(); + + glViewport(0, 0, SCREENWIDTH, SCREENHEIGHT); + + glClearColor(0.0f, 0.5f, 0.5f, 1.0f); + glClearDepth(1.0f); + + glEnable(GL_BLEND); + glEnable(GL_DEPTH_CLAMP_NV); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // proff_dis + glShadeModel(GL_FLAT); + gld_EnableTexture2D(GL_TEXTURE0_ARB, true); + glDepthFunc(GL_LEQUAL); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GEQUAL,0.5f); + glDisable(GL_CULL_FACE); + glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); + + glTexGenfv(GL_Q,GL_EYE_PLANE,params); + glTexGenf(GL_S,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); + glTexGenf(GL_T,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); + glTexGenf(GL_Q,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); + + //e6y + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + gld_Finish(); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(0.0f, 0.5f, 0.5f, 1.0f); + + // e6y + // if you have a prior crash in the game, + // you can restore the gamma values to at least a linear value + // with -resetgamma command-line switch + gld_ResetGammaRamp(); + + gld_InitLightTable(); + gld_InitSky(); + M_ChangeLightMode(); + M_ChangeAllowFog(); + + gld_InitDetail(); + gld_InitShadows(); + +#ifdef HAVE_LIBSDL2_IMAGE + gld_InitMapPics(); + gld_InitHiRes(); +#endif + + // Create FBO object and associated render targets +#ifdef USE_FBO_TECHNIQUE + gld_InitFBO(); + I_AtExit(gld_FreeScreenSizeFBO, true); +#endif + + if(!gld_LoadGLDefs("GLBDEFS")) + { + gld_LoadGLDefs("GLDEFS"); + } + + gld_ResetLastTexture(); + + I_AtExit(gld_CleanMemory, true); //e6y +} + +void gld_InitCommandLine(void) +{ +} + +// +// Textured automap +// + +static int C_DECL dicmp_visible_subsectors_by_pic(const void *a, const void *b) +{ + return (*((const subsector_t *const *)b))->sector->floorpic - + (*((const subsector_t *const *)a))->sector->floorpic; +} + +static int visible_subsectors_count_prev = -1; +void gld_ResetTexturedAutomap(void) +{ + visible_subsectors_count_prev = -1; +} + +void gld_MapDrawSubsectors(player_t *plr, int fx, int fy, fixed_t mx, fixed_t my, int fw, int fh, fixed_t scale) +{ + extern int ddt_cheating; + + static subsector_t **visible_subsectors = NULL; + static int visible_subsectors_size = 0; + int visible_subsectors_count; + + int i; + float alpha; + float coord_scale; + GLTexture *gltexture; + + alpha = (float)((automapmode & am_overlay) ? map_textured_overlay_trans : map_textured_trans) / 100.0f; + if (alpha == 0) + return; + + if (numsubsectors > visible_subsectors_size) + { + visible_subsectors_size = numsubsectors; + visible_subsectors = realloc(visible_subsectors, visible_subsectors_size * sizeof(visible_subsectors[0])); + } + + visible_subsectors_count = 0; + if (ddt_cheating) + { + visible_subsectors_count = numsubsectors; + } + else + { + for (i = 0; i < numsubsectors; i++) + { + visible_subsectors_count += map_subsectors[i]; + } + } + + // Do not sort static visible_subsectors array at all + // if there are no new visible subsectors. + if (visible_subsectors_count != visible_subsectors_count_prev) + { + visible_subsectors_count_prev = visible_subsectors_count; + + visible_subsectors_count = 0; + for (i = 0; i < numsubsectors; i++) + { + if (map_subsectors[i] || ddt_cheating) + { + visible_subsectors[visible_subsectors_count++] = &subsectors[i]; + } + } + + // sort subsectors by texture + qsort(visible_subsectors, visible_subsectors_count, + sizeof(visible_subsectors[0]), dicmp_visible_subsectors_by_pic); + } + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glScissor(fx, SCREENHEIGHT - (fy + fh), fw, fh); + glEnable(GL_SCISSOR_TEST); + + if (automapmode & am_rotate) + { + float pivotx = (float)(fx + fw / 2); + float pivoty = (float)(fy + fh / 2); + + float rot = -(float)(ANG90 - viewangle) / (float)(1u << 31) * 180.0f; + + // Apply the automap's rotation to the vertexes. + glTranslatef(pivotx, pivoty, 0.0f); + glRotatef(rot, 0.0f, 0.0f, 1.0f); + glTranslatef(-pivotx, -pivoty, 0.0f); + } + + glTranslatef( + (float)fx - (float)mx / (float)FRACUNIT * (float)scale / (float)FRACUNIT, + (float)fy + (float)fh + (float)my / (float)FRACUNIT * (float)scale / (float)FRACUNIT, + 0); + coord_scale = (float)scale / (float)(1<sector->bbox[BOXLEFT] > am_frame.bbox[BOXRIGHT] || + sub->sector->bbox[BOXRIGHT] < am_frame.bbox[BOXLEFT] || + sub->sector->bbox[BOXBOTTOM] > am_frame.bbox[BOXTOP] || + sub->sector->bbox[BOXTOP] < am_frame.bbox[BOXBOTTOM]) + { + continue; + } + + gltexture = gld_RegisterFlat(flattranslation[sub->sector->floorpic], true); + if (gltexture) + { + sector_t tempsec; + int floorlight; + float light; + float floor_uoffs, floor_voffs; + int loopnum; + + // For lighting and texture determination + sector_t *sec = R_FakeFlat(sub->sector, &tempsec, &floorlight, NULL, false); + + gld_BindFlat(gltexture, 0); + light = gld_Calc2DLightLevel(floorlight); + gld_StaticLightAlpha(light, alpha); + + // Find texture origin. + floor_uoffs = (float)sec->floor_xoffs/(float)(FRACUNIT*64); + floor_voffs = (float)sec->floor_yoffs/(float)(FRACUNIT*64); + + for (loopnum = 0; loopnum < subsectorloops[ssidx].loopcount; loopnum++) + { + int vertexnum; + GLLoopDef *currentloop = &subsectorloops[ssidx].loops[loopnum]; + + if (!currentloop) + continue; + + // set the mode (GL_TRIANGLE_FAN) + glBegin(currentloop->mode); + // go through all vertexes of this loop + for (vertexnum = currentloop->vertexindex; vertexnum < (currentloop->vertexindex + currentloop->vertexcount); vertexnum++) + { + glTexCoord2f(flats_vbo[vertexnum].u + floor_uoffs, flats_vbo[vertexnum].v + floor_voffs); + glVertex3f(flats_vbo[vertexnum].x, flats_vbo[vertexnum].z, 0); + } + glEnd(); + } + } + } + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glDisable(GL_SCISSOR_TEST); +} + +void gld_DrawTriangleStrip(GLWall *wall, gl_strip_coords_t *c) +{ + glBegin(GL_TRIANGLE_STRIP); + + glTexCoord2fv((const GLfloat*)&c->t[0]); + glVertex3fv((const GLfloat*)&c->v[0]); + + glTexCoord2fv((const GLfloat*)&c->t[1]); + glVertex3fv((const GLfloat*)&c->v[1]); + + glTexCoord2fv((const GLfloat*)&c->t[2]); + glVertex3fv((const GLfloat*)&c->v[2]); + + glTexCoord2fv((const GLfloat*)&c->t[3]); + glVertex3fv((const GLfloat*)&c->v[3]); + + glEnd(); +} + +void gld_DrawNumPatch_f(float x, float y, int lump, int cm, enum patch_translation_e flags) +{ + GLTexture *gltexture; + float fU1,fU2,fV1,fV2; + float width,height; + float xpos, ypos; + int cmap; + int leftoffset, topoffset; + + //e6y + dboolean bFakeColormap; + + cmap = ((flags & VPT_TRANS) ? cm : CR_DEFAULT); + gltexture=gld_RegisterPatch(lump, cmap, false); + gld_BindPatch(gltexture, cmap); + + if (!gltexture) + return; + fV1=0.0f; + fV2=gltexture->scaleyfac; + if (flags & VPT_FLIP) + { + fU1=gltexture->scalexfac; + fU2=0.0f; + } + else + { + fU1=0.0f; + fU2=gltexture->scalexfac; + } + + if (flags & VPT_NOOFFSET) + { + leftoffset = 0; + topoffset = 0; + } + else + { + leftoffset = gltexture->leftoffset; + topoffset = gltexture->topoffset; + } + + // [FG] automatically center wide patches without horizontal offset + if (gltexture->width > 320 && leftoffset == 0) + x -= (float)(gltexture->width - 320) / 2; + + if (flags & VPT_STRETCH_MASK) + { + stretch_param_t *params = &stretch_params[flags & VPT_ALIGN_MASK]; + + xpos = (float)((x - leftoffset) * params->video->width) / 320.0f + params->deltax1; + ypos = (float)((y - topoffset) * params->video->height) / 200.0f + params->deltay1; + width = (float)(gltexture->realtexwidth * params->video->width) / 320.0f; + height = (float)(gltexture->realtexheight * params->video->height) / 200.0f; + } + else + { + xpos = (float)(x - leftoffset); + ypos = (float)(y - topoffset); + width = (float)(gltexture->realtexwidth); + height = (float)(gltexture->realtexheight); + } + + bFakeColormap = + (gltexture->flags & GLTEXTURE_HIRES) && + (lumpinfo[lump].flags & LUMP_CM2RGB); + if (bFakeColormap) + { + if (!(flags & VPT_TRANS) && (cm != CR_GRAY)) + { + cm = CR_RED; + } + glColor3f(cm2RGB[cm][0], cm2RGB[cm][1], cm2RGB[cm][2]);//, cm2RGB[cm][3]); + } + else + { + // e6y + // This is a workaround for some on-board Intel video cards. + // Do you know more elegant solution? + glColor3f(1.0f, 1.0f, 1.0f); + } + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(fU1, fV1); glVertex2f((xpos),(ypos)); + glTexCoord2f(fU1, fV2); glVertex2f((xpos),(ypos+height)); + glTexCoord2f(fU2, fV1); glVertex2f((xpos+width),(ypos)); + glTexCoord2f(fU2, fV2); glVertex2f((xpos+width),(ypos+height)); + glEnd(); + + if (bFakeColormap) + { + glColor3f(1.0f,1.0f,1.0f); + } +} + +void gld_DrawNumPatch(int x, int y, int lump, int cm, enum patch_translation_e flags) +{ + gld_DrawNumPatch_f((float)x, (float)y, lump, cm, flags); +} + +void gld_FillFlat(int lump, int x, int y, int width, int height, enum patch_translation_e flags) +{ + GLTexture *gltexture; + float fU1, fU2, fV1, fV2; + + //e6y: Boom colormap should not be applied for background + int saved_boom_cm = boom_cm; + boom_cm = 0; + + gltexture = gld_RegisterFlat(lump, false); + gld_BindFlat(gltexture, 0); + + //e6y + boom_cm = saved_boom_cm; + + if (!gltexture) + return; + + if (flags & VPT_STRETCH) + { + x = x * SCREENWIDTH / 320; + y = y * SCREENHEIGHT / 200; + width = width * SCREENWIDTH / 320; + height = height * SCREENHEIGHT / 200; + } + + fU1 = 0; + fV1 = 0; + fU2 = (float)width / (float)gltexture->realtexwidth; + fV2 = (float)height / (float)gltexture->realtexheight; + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(fU1, fV1); glVertex2f((float)(x),(float)(y)); + glTexCoord2f(fU1, fV2); glVertex2f((float)(x),(float)(y + height)); + glTexCoord2f(fU2, fV1); glVertex2f((float)(x + width),(float)(y)); + glTexCoord2f(fU2, fV2); glVertex2f((float)(x + width),(float)(y + height)); + glEnd(); +} + +void gld_FillPatch(int lump, int x, int y, int width, int height, enum patch_translation_e flags) +{ + GLTexture *gltexture; + float fU1, fU2, fV1, fV2; + + //e6y: Boom colormap should not be applied for background + int saved_boom_cm = boom_cm; + boom_cm = 0; + + gltexture = gld_RegisterPatch(lump, CR_DEFAULT, false); + gld_BindPatch(gltexture, CR_DEFAULT); + + if (!gltexture) + return; + + x = x - gltexture->leftoffset; + y = y - gltexture->topoffset; + + //e6y + boom_cm = saved_boom_cm; + + if (flags & VPT_STRETCH) + { + x = x * SCREENWIDTH / 320; + y = y * SCREENHEIGHT / 200; + width = width * SCREENWIDTH / 320; + height = height * SCREENHEIGHT / 200; + } + + fU1 = 0; + fV1 = 0; + fU2 = (float)width / (float)gltexture->realtexwidth; + fV2 = (float)height / (float)gltexture->realtexheight; + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(fU1, fV1); glVertex2f((float)(x),(float)(y)); + glTexCoord2f(fU1, fV2); glVertex2f((float)(x),(float)(y + height)); + glTexCoord2f(fU2, fV1); glVertex2f((float)(x + width),(float)(y)); + glTexCoord2f(fU2, fV2); glVertex2f((float)(x + width),(float)(y + height)); + glEnd(); +} + +void gld_DrawLine_f(float x0, float y0, float x1, float y1, int BaseColor) +{ +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + const unsigned char *playpal = V_GetPlaypal(); + unsigned char r, g, b, a; + map_line_t *line; + + a = ((automapmode & am_overlay) ? map_lines_overlay_trans * 255 / 100 : 255); + if (a == 0) + return; + + r = playpal[3 * BaseColor + 0]; + g = playpal[3 * BaseColor + 1]; + b = playpal[3 * BaseColor + 2]; + + line = M_ArrayGetNewItem(&map_lines, sizeof(line[0])); + + line->point[0].x = x0; + line->point[0].y = y0; + line->point[0].r = r; + line->point[0].g = g; + line->point[0].b = b; + line->point[0].a = a; + + line->point[1].x = x1; + line->point[1].y = y1; + line->point[1].r = r; + line->point[1].g = g; + line->point[1].b = b; + line->point[1].a = a; +#else + const unsigned char *playpal = V_GetPlaypal(); + + float alpha = ((automapmode & am_overlay) ? map_lines_overlay_trans / 100.0f : 1.0f); + if (alpha == 0) + return; + + glColor4f((float)playpal[3*BaseColor]/255.0f, + (float)playpal[3*BaseColor+1]/255.0f, + (float)playpal[3*BaseColor+2]/255.0f, + alpha); + glBegin(GL_LINES); + glVertex2f( x0, y0 ); + glVertex2f( x1, y1 ); + glEnd(); +#endif +} + +void gld_DrawLine(int x0, int y0, int x1, int y1, int BaseColor) +{ + gld_DrawLine_f((float)x0, (float)y0, (float)x1, (float)y1, BaseColor); +} + +void gld_DrawWeapon(int weaponlump, vissprite_t *vis, int lightlevel) +{ + GLTexture *gltexture; + float fU1,fU2,fV1,fV2; + int x1,y1,x2,y2; + float light; + + gltexture=gld_RegisterPatch(firstspritelump+weaponlump, CR_DEFAULT, false); + if (!gltexture) + return; + gld_BindPatch(gltexture, CR_DEFAULT); + fU1=0; + fV1=0; + fU2=gltexture->scalexfac; + fV2=gltexture->scaleyfac; + // e6y + // More precise weapon drawing: + // Shotgun from DSV3_War looks correctly now. Especially during movement. + // There is no more line of graphics under certain weapons. + x1=viewwindowx+vis->x1; + x2=x1+(int)((float)gltexture->realtexwidth*pspritexscale_f); + y1=viewwindowy+centery-(int)(((float)vis->texturemid/(float)FRACUNIT)*pspriteyscale_f); + y2=y1+(int)((float)gltexture->realtexheight*pspriteyscale_f)+1; + // e6y: don't do the gamma table correction on the lighting + light = (float)lightlevel / 255.0f; + + // e6y + // Fix of no warning (flashes between shadowed and solid) + // when invisibility is about to go + if (/*(viewplayer->mo->flags & MF_SHADOW) && */!vis->colormap) + { + glBlendFunc(gl_fuzzsfactors[gl_weaponspritefuzzmode], + gl_fuzzdfactors[gl_weaponspritefuzzmode]); + glAlphaFunc(GL_GEQUAL,0.1f); + //glColor4f(0.2f,0.2f,0.2f,(float)tran_filter_pct/100.0f); + glColor4f(0.2f,0.2f,0.2f,0.33f); + } + else + { + if (viewplayer->mo->flags & MF_TRANSLUCENT) + gld_StaticLightAlpha(light,(float)tran_filter_pct/100.0f); + else + gld_StaticLight(light); + } + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(fU1, fV1); glVertex2f((float)(x1),(float)(y1)); + glTexCoord2f(fU1, fV2); glVertex2f((float)(x1),(float)(y2)); + glTexCoord2f(fU2, fV1); glVertex2f((float)(x2),(float)(y1)); + glTexCoord2f(fU2, fV2); glVertex2f((float)(x2),(float)(y2)); + glEnd(); + if(!vis->colormap) + { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glAlphaFunc(GL_GEQUAL,0.5f); + } + glColor3f(1.0f,1.0f,1.0f); +} + +void gld_FillBlock(int x, int y, int width, int height, int col) +{ + const unsigned char *playpal = V_GetPlaypal(); + + gld_EnableTexture2D(GL_TEXTURE0_ARB, false); + glColor3f((float)playpal[3*col]/255.0f, + (float)playpal[3*col+1]/255.0f, + (float)playpal[3*col+2]/255.0f); + glBegin(GL_TRIANGLE_STRIP); + glVertex2i( x, y ); + glVertex2i( x, y+height ); + glVertex2i( x+width, y ); + glVertex2i( x+width, y+height ); + glEnd(); + glColor3f(1.0f,1.0f,1.0f); + gld_EnableTexture2D(GL_TEXTURE0_ARB, true); +} + +void gld_SetPalette(int palette) +{ + static int last_palette = 0; + extra_red=0.0f; + extra_green=0.0f; + extra_blue=0.0f; + extra_alpha=0.0f; + if (palette < 0) + palette = last_palette; + last_palette = palette; + if (gl_shared_texture_palette) { + const unsigned char *playpal; + unsigned char pal[1024]; + int i; + + playpal = V_GetPlaypal(); + playpal += (768*palette); + for (i=0; i<256; i++) { + int col; + + if (fixedcolormap) + col = fixedcolormap[i]; + else if (fullcolormap) + col = fullcolormap[i]; + else + col = i; + pal[i*4+0] = playpal[col*3+0]; + pal[i*4+1] = playpal[col*3+1]; + pal[i*4+2] = playpal[col*3+2]; + pal[i*4+3] = 255; + } + pal[transparent_pal_index*4+0]=0; + pal[transparent_pal_index*4+1]=0; + pal[transparent_pal_index*4+2]=0; + pal[transparent_pal_index*4+3]=0; + GLEXT_glColorTableEXT(GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, pal); + } else { + if (palette>0) + { + if (palette<=8) + { + extra_red=(float)palette/2.0f; + extra_green=0.0f; + extra_blue=0.0f; + extra_alpha=(float)palette/10.0f; + } + else + if (palette<=12) + { + palette=palette-8; + extra_red=(float)palette*1.0f; + extra_green=(float)palette*0.8f; + extra_blue=(float)palette*0.1f; + extra_alpha=(float)palette/11.0f; + } + else + if (palette==13) + { + extra_red=0.4f; + extra_green=1.0f; + extra_blue=0.0f; + extra_alpha=0.2f; + } + } + if (extra_red>1.0f) + extra_red=1.0f; + if (extra_green>1.0f) + extra_green=1.0f; + if (extra_blue>1.0f) + extra_blue=1.0f; + if (extra_alpha>1.0f) + extra_alpha=1.0f; + } +} + +unsigned char *gld_ReadScreen(void) +{ // NSM convert to static + static unsigned char *scr = NULL; + static unsigned char *buffer = NULL; + static int scr_size = 0; + static int buffer_size = 0; + + int i, size; + + size = SCREENWIDTH * 3; + if (!buffer || size > buffer_size) + { + buffer_size = size; + buffer = realloc (buffer, size); + } + size = SCREENWIDTH * SCREENHEIGHT * 3; + if (!scr || size > scr_size) + { + scr_size = size; + scr = realloc (scr, size); + } + + if (buffer && scr) + { + GLint pack_aligment; + glGetIntegerv(GL_PACK_ALIGNMENT, &pack_aligment); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + + glFlush(); + glReadPixels(0, 0, SCREENWIDTH, SCREENHEIGHT, GL_RGB, GL_UNSIGNED_BYTE, scr); + + glPixelStorei(GL_PACK_ALIGNMENT, pack_aligment); + + gld_ApplyGammaRamp(scr, SCREENWIDTH * 3, SCREENWIDTH, SCREENHEIGHT); + + for (i=0; i>ANGLETOFINESHIFT)*360.0f/FINEANGLES; + inv_yaw=180.0f-yaw; + cos_inv_yaw = (float)cos(inv_yaw * M_PI / 180.f); + sin_inv_yaw = (float)sin(inv_yaw * M_PI / 180.f); + + gl_spriteindex = 0; + + //e6y: fog in frame + gl_use_fog = !gl_compatibility && + (gl_fog || gl_lightmode == gl_lightmode_fogbased) && + !frame_fixedcolormap && !boom_cm; + +//e6y + mlook_or_fov = GetMouseLook() || (render_fov != FOV90); + if(!mlook_or_fov) + { + pitch = 0.0f; + paperitems_pitch = 0.0f; + + skyXShift = -2.0f * ((yaw + 90.0f) / 90.0f); + skyYShift = 200.0f / 319.5f; + } + else + { + float f = viewPitch * 2 + 50 / skyscale; + f = BETWEEN(0, 127, f); + skyXShift = -2.0f * ((yaw + 90.0f) / 90.0f / skyscale); + skyYShift = f / 128.0f + 200.0f / 320.0f / skyscale; + + pitch = (float)(viewpitch>>ANGLETOFINESHIFT) * 360.0f / FINEANGLES; + paperitems_pitch = ((pitch > 87.0f && pitch <= 90.0f) ? 87.0f : pitch); + viewPitch = (pitch > 180 ? pitch - 360 : pitch); + } + cos_paperitems_pitch = (float)cos(paperitems_pitch * M_PI / 180.f); + sin_paperitems_pitch = (float)sin(paperitems_pitch * M_PI / 180.f); + gl_mask_sprite_threshold_f = (gl_sprite_blend ? (float)gl_mask_sprite_threshold / 100.0f : 0.5f); + + gld_InitFrameSky(); + + invul_method = 0; + if (players[displayplayer].fixedcolormap == 32) + { + if (gl_boom_colormaps && !gl_has_hires) + { + invul_method = INVUL_CM; + } + else + { + if (gl_version >= OPENGL_VERSION_1_3) + { + invul_method = INVUL_BW; + } + else + { + invul_method = INVUL_INV; + } + } + } + +#ifdef USE_FBO_TECHNIQUE + motion_blur.enabled = gl_use_motionblur && + ((motion_blur.curr_speed_pow2 > motion_blur.minspeed_pow2) || + (abs(players[displayplayer].cmd.angleturn) > motion_blur.minangle)); + + SceneInTexture = (gl_ext_framebuffer_object) && + ((invul_method & INVUL_BW) || (motion_blur.enabled)); + + // Vortex: Set FBO object + if (SceneInTexture) + { + GLEXT_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, glSceneImageFBOTexID); + } +#endif + + SetFrameTextureMode(); + + gld_Clear(); + + glEnable(GL_DEPTH_TEST); + + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(projMatrix); + + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(modelMatrix); + + rendermarker++; + scene_has_overlapped_sprites = false; + scene_has_wall_details = 0; + scene_has_flat_details = 0; +} + +//e6y +void gld_ProcessExtraAlpha(void) +{ + if (extra_alpha>0.0f && !invul_method) + { + float current_color[4]; + glGetFloatv(GL_CURRENT_COLOR, current_color); + glDisable(GL_ALPHA_TEST); + glColor4f(extra_red, extra_green, extra_blue, extra_alpha); + gld_EnableTexture2D(GL_TEXTURE0_ARB, false); + glBegin(GL_TRIANGLE_STRIP); + glVertex2f( 0.0f, 0.0f); + glVertex2f( 0.0f, (float)SCREENHEIGHT); + glVertex2f( (float)SCREENWIDTH, 0.0f); + glVertex2f( (float)SCREENWIDTH, (float)SCREENHEIGHT); + glEnd(); + gld_EnableTexture2D(GL_TEXTURE0_ARB, true); + glEnable(GL_ALPHA_TEST); + glColor4f(current_color[0], current_color[1], current_color[2], current_color[3]); + } +} + +//e6y +static void gld_InvertScene(void) +{ + glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); + glColor4f(1,1,1,1); + gld_EnableTexture2D(GL_TEXTURE0_ARB, false); + glBegin(GL_TRIANGLE_STRIP); + glVertex2f( 0.0f, 0.0f); + glVertex2f( 0.0f, (float)SCREENHEIGHT); + glVertex2f( (float)SCREENWIDTH, 0.0f); + glVertex2f( (float)SCREENWIDTH, (float)SCREENHEIGHT); + glEnd(); + gld_EnableTexture2D(GL_TEXTURE0_ARB, true); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void gld_EndDrawScene(void) +{ + glDisable(GL_POLYGON_SMOOTH); + + glViewport(0, 0, SCREENWIDTH, SCREENHEIGHT); + gl_EnableFog(false); + gld_Set2DMode(); + + if (!viewangleoffset && !viewpitchoffset) + { // don't draw on side views + glsl_SetActiveShader(sh_main); + R_DrawPlayerSprites(); + glsl_SetActiveShader(NULL); + } + + // e6y + // Effect of invulnerability uses a colormap instead of hard-coding now + // See nuts.wad + // http://www.doomworld.com/idgames/index.php?id=11402 + +#ifdef USE_FBO_TECHNIQUE + // Vortex: Black and white effect + if (SceneInTexture) + { + // Vortex: Restore original RT + GLEXT_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + glBindTexture(GL_TEXTURE_2D, glSceneImageTextureFBOTexID); + + // Setup blender + if (invul_method & INVUL_BW) + { + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_DOT3_RGB); + glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_RGB,GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_RGB,GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE1_RGB,GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_RGB,GL_SRC_COLOR); + + glColor3f(0.3f, 0.3f, 0.4f); + } + else + { + glColor3f(1.0f, 1.0f, 1.0f); + } + + //e6y: motion bloor effect for strafe50 + if (motion_blur.enabled) + { + extern int renderer_fps; + static float motionblur_alpha = 1.0f; + + if (realframe) + { + motionblur_alpha = (float)((atan(-renderer_fps / motion_blur.att_a)) / motion_blur.att_b) + motion_blur.att_c; + } + + glBlendFunc(GL_CONSTANT_ALPHA_EXT, GL_ONE_MINUS_CONSTANT_ALPHA_EXT); + GLEXT_glBlendColorEXT(1.0f, 1.0f, 1.0f, motionblur_alpha); + } + + glBegin(GL_TRIANGLE_STRIP); + { + glTexCoord2f(0.0f, 1.0f); glVertex2f(0.0f, 0.0f); + glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0f, (float)SCREENHEIGHT); + glTexCoord2f(1.0f, 1.0f); glVertex2f((float)SCREENWIDTH, 0.0f); + glTexCoord2f(1.0f, 0.0f); glVertex2f((float)SCREENWIDTH, (float)SCREENHEIGHT); + } + glEnd(); + + + if (motion_blur.enabled) + { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); + } + else +#endif + { + if (invul_method & INVUL_INV) + { + gld_InvertScene(); + } + if (invul_method & INVUL_BW) + { + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); + } + } + + glColor3f(1.0f,1.0f,1.0f); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_ALPHA_TEST); + if (gl_shared_texture_palette) + glDisable(GL_SHARED_TEXTURE_PALETTE_EXT); +} + +static void gld_AddDrawWallItem(GLDrawItemType itemtype, void *itemdata) +{ + if (gl_blend_animations) + { + anim_t *anim; + int currpic, nextpic; + GLWall *wall = (GLWall*)itemdata; + float oldalpha = wall->alpha; + + switch (itemtype) + { + case GLDIT_FWALL: + anim = anim_flats[wall->gltexture->index - firstflat].anim; + if (anim) + { + wall->alpha = 1.0f - ((float)tic_vars.frac + ((leveltime - 1) % anim->speed) * 65536.0f) / (65536.0f * anim->speed); + gld_AddDrawItem(GLDIT_FAWALL, itemdata); + + currpic = wall->gltexture->index - firstflat - anim->basepic; + nextpic = anim->basepic + (currpic + 1) % anim->numpics; + wall->alpha = oldalpha; + wall->gltexture = gld_RegisterFlat(nextpic, true); + } + break; + case GLDIT_WALL: + case GLDIT_MWALL: + anim = anim_textures[wall->gltexture->index].anim; + if (anim) + { + if (itemtype == GLDIT_WALL || itemtype == GLDIT_MWALL) + { + wall->alpha = 1.0f - ((float)tic_vars.frac + ((leveltime - 1) % anim->speed) * 65536.0f) / (65536.0f * anim->speed); + gld_AddDrawItem(GLDIT_AWALL, itemdata); + + currpic = wall->gltexture->index - anim->basepic; + nextpic = anim->basepic + (currpic + 1) % anim->numpics; + wall->alpha = oldalpha; + wall->gltexture = gld_RegisterTexture(nextpic, true, false); + } + } + break; + } + } + + if (((GLWall*)itemdata)->gltexture->detail) + scene_has_wall_details++; + + gld_AddDrawItem(itemtype, itemdata); +} + +/***************** + * * + * Walls * + * * + *****************/ + +static void gld_DrawWall(GLWall *wall) +{ + int has_detail; + unsigned int flags; + + rendered_segs++; + + has_detail = + scene_has_details && + gl_arb_multitexture && + (wall->flag < GLDWF_SKY) && + (wall->gltexture->detail) && + gld_IsDetailVisible(xCamera, yCamera, + wall->glseg->x1, wall->glseg->z1, + wall->glseg->x2, wall->glseg->z2); + + // Do not repeat middle texture vertically + // to avoid visual glitches for textures with holes + if ((wall->flag == GLDWF_M2S) && (wall->flag < GLDWF_SKY)) + flags = GLTEXTURE_CLAMPY; + else + flags = 0; + + gld_BindTexture(wall->gltexture, flags); + gld_BindDetailARB(wall->gltexture, has_detail); + + if (!wall->gltexture) + { + glColor4f(1.0f,0.0f,0.0f,1.0f); + } + + if (has_detail) + { + gld_DrawWallWithDetail(wall); + } + else + { + if ((wall->flag == GLDWF_TOPFLUD) || (wall->flag == GLDWF_BOTFLUD)) + { + gl_strip_coords_t c; + + gld_BindFlat(wall->gltexture, 0); + + gld_SetupFloodStencil(wall); + gld_SetupFloodedPlaneCoords(wall, &c); + gld_SetupFloodedPlaneLight(wall); + gld_DrawTriangleStrip(wall, &c); + + gld_ClearFloodStencil(wall); + } + else + { + gld_StaticLightAlpha(wall->light, wall->alpha); + + glBegin(GL_TRIANGLE_FAN); + + // lower left corner + glTexCoord2f(wall->ul,wall->vb); + glVertex3f(wall->glseg->x1,wall->ybottom,wall->glseg->z1); + + // split left edge of wall + if (!wall->glseg->fracleft) + gld_SplitLeftEdge(wall, false); + + // upper left corner + glTexCoord2f(wall->ul,wall->vt); + glVertex3f(wall->glseg->x1,wall->ytop,wall->glseg->z1); + + // upper right corner + glTexCoord2f(wall->ur,wall->vt); + glVertex3f(wall->glseg->x2,wall->ytop,wall->glseg->z2); + + // split right edge of wall + if (!wall->glseg->fracright) + gld_SplitRightEdge(wall, false); + + // lower right corner + glTexCoord2f(wall->ur,wall->vb); + glVertex3f(wall->glseg->x2,wall->ybottom,wall->glseg->z2); + + glEnd(); + } + } +} + +#define LINE seg->linedef +#define CALC_Y_VALUES(w, lineheight, floor_height, ceiling_height)\ + (w).ytop=((float)(ceiling_height)/(float)MAP_SCALE)+SMALLDELTA;\ + (w).ybottom=((float)(floor_height)/(float)MAP_SCALE)-SMALLDELTA;\ + lineheight=((float)fabs(((ceiling_height)/(float)FRACUNIT)-((floor_height)/(float)FRACUNIT))) + +#define OU(w,seg) (((float)((seg)->sidedef->textureoffset)/(float)FRACUNIT)/(float)(w).gltexture->buffer_width) +#define OV(w,seg) (((float)((seg)->sidedef->rowoffset)/(float)FRACUNIT)/(float)(w).gltexture->buffer_height) +#define OV_PEG(w,seg,v_offset) (OV((w),(seg))-(((float)(v_offset)/(float)FRACUNIT)/(float)(w).gltexture->buffer_height)) +#define URUL(w, seg, backseg, linelength)\ + if (backseg){\ + (w).ur=OU((w),(seg));\ + (w).ul=(w).ur+((linelength)/(float)(w).gltexture->buffer_width);\ + }else{\ + (w).ul=OU((w),(seg));\ + (w).ur=(w).ul+((linelength)/(float)(w).gltexture->buffer_width);\ + } + +#define CALC_TEX_VALUES_TOP(w, seg, backseg, peg, linelength, lineheight)\ + (w).flag=GLDWF_TOP;\ + URUL(w, seg, backseg, linelength);\ + if (peg){\ + (w).vb=OV((w),(seg))+((w).gltexture->scaleyfac);\ + (w).vt=((w).vb-((float)(lineheight)/(float)(w).gltexture->buffer_height));\ + }else{\ + (w).vt=OV((w),(seg));\ + (w).vb=(w).vt+((float)(lineheight)/(float)(w).gltexture->buffer_height);\ + } + +#define CALC_TEX_VALUES_MIDDLE1S(w, seg, backseg, peg, linelength, lineheight)\ + (w).flag=GLDWF_M1S;\ + URUL(w, seg, backseg, linelength);\ + if (peg){\ + (w).vb=OV((w),(seg))+((w).gltexture->scaleyfac);\ + (w).vt=((w).vb-((float)(lineheight)/(float)(w).gltexture->buffer_height));\ + }else{\ + (w).vt=OV((w),(seg));\ + (w).vb=(w).vt+((float)(lineheight)/(float)(w).gltexture->buffer_height);\ + } + +#define CALC_TEX_VALUES_BOTTOM(w, seg, backseg, peg, linelength, lineheight, v_offset)\ + (w).flag=GLDWF_BOT;\ + URUL(w, seg, backseg, linelength);\ + if (peg){\ + (w).vb=OV_PEG((w),(seg),(v_offset))+((w).gltexture->scaleyfac);\ + (w).vt=((w).vb-((float)(lineheight)/(float)(w).gltexture->buffer_height));\ + }else{\ + (w).vt=OV((w),(seg));\ + (w).vb=(w).vt+((float)(lineheight)/(float)(w).gltexture->buffer_height);\ + } + +void gld_AddWall(seg_t *seg) +{ + GLWall wall; + GLTexture *temptex; + sector_t *frontsector; + sector_t *backsector; + sector_t ftempsec; // needed for R_FakeFlat + sector_t btempsec; // needed for R_FakeFlat + float lineheight, linelength; + int rellight = 0; + int backseg; + dboolean fix_sky_bleed = false; + + int side = (seg->sidedef == &sides[seg->linedef->sidenum[0]] ? 0 : 1); + if (linerendered[side][seg->linedef->iLineID] == rendermarker) + return; + linerendered[side][seg->linedef->iLineID] = rendermarker; + linelength = lines[seg->linedef->iLineID].texel_length; + wall.glseg=&gl_lines[seg->linedef->iLineID]; + backseg = seg->sidedef != &sides[seg->linedef->sidenum[0]]; + + if (!seg->frontsector) + return; + frontsector=R_FakeFlat(seg->frontsector, &ftempsec, NULL, NULL, false); // for boom effects + if (!frontsector) + return; + + // e6y: fake contrast stuff + // Original doom added/removed one light level ((1<linedef->dx == 0 ? +gl_rellight : seg->linedef->dy==0 ? -gl_rellight : 0; + } + wall.light=gld_CalcLightLevel(frontsector->lightlevel+rellight+(extralight<<5)); + wall.fogdensity = gld_CalcFogDensity(frontsector, + frontsector->lightlevel + (gl_lightmode == gl_lightmode_fogbased ? rellight : 0), + GLDIT_WALL); + wall.alpha=1.0f; + wall.gltexture=NULL; + wall.seg = seg; //e6y + + if (!seg->backsector) /* onesided */ + { + if (frontsector->ceilingpic==skyflatnum) + { + wall.ytop=MAXCOORD; + wall.ybottom=(float)frontsector->ceilingheight/MAP_SCALE; + gld_AddSkyTexture(&wall, frontsector->sky, frontsector->sky, SKY_CEILING); + } + if (frontsector->floorpic==skyflatnum) + { + wall.ytop=(float)frontsector->floorheight/MAP_SCALE; + wall.ybottom=-MAXCOORD; + gld_AddSkyTexture(&wall, frontsector->sky, frontsector->sky, SKY_FLOOR); + } + temptex=gld_RegisterTexture(texturetranslation[seg->sidedef->midtexture], true, false); + if (temptex && frontsector->ceilingheight > frontsector->floorheight) + { + wall.gltexture=temptex; + CALC_Y_VALUES(wall, lineheight, frontsector->floorheight, frontsector->ceilingheight); + CALC_TEX_VALUES_MIDDLE1S( + wall, seg, backseg, (LINE->flags & ML_DONTPEGBOTTOM)>0, + linelength, lineheight + ); + gld_AddDrawWallItem(GLDIT_WALL, &wall); + } + } + else /* twosided */ + { + sector_t *fs, *bs; + int toptexture, midtexture, bottomtexture; + fixed_t floor_height,ceiling_height; + fixed_t max_floor, min_floor; + fixed_t max_ceiling, min_ceiling; + //fixed_t max_floor_tex, min_ceiling_tex; + + backsector=R_FakeFlat(seg->backsector, &btempsec, NULL, NULL, true); // for boom effects + if (!backsector) + return; + + if (frontsector->floorheight > backsector->floorheight) + { + max_floor = frontsector->floorheight; + min_floor = backsector->floorheight; + } + else + { + max_floor = backsector->floorheight; + min_floor = frontsector->floorheight; + } + + if (frontsector->ceilingheight > backsector->ceilingheight) + { + max_ceiling = frontsector->ceilingheight; + min_ceiling = backsector->ceilingheight; + } + else + { + max_ceiling = backsector->ceilingheight; + min_ceiling = frontsector->ceilingheight; + } + + //max_floor_tex = max_floor + seg->sidedef->rowoffset; + //min_ceiling_tex = min_ceiling + seg->sidedef->rowoffset; + + if (backseg) + { + fs = backsector; + bs = frontsector; + } + else + { + fs = frontsector; + bs = backsector; + } + + toptexture = texturetranslation[seg->sidedef->toptexture]; + midtexture = texturetranslation[seg->sidedef->midtexture]; + bottomtexture = texturetranslation[seg->sidedef->bottomtexture]; + + /* toptexture */ + ceiling_height=frontsector->ceilingheight; + floor_height=backsector->ceilingheight; + if (frontsector->ceilingpic==skyflatnum)// || backsector->ceilingpic==skyflatnum) + { + wall.ytop= MAXCOORD; + if ( + // e6y + // There is no more HOM in the starting area on Memento Mori map29 and on map30. + // Old code: + // (backsector->ceilingheight==backsector->floorheight) && + // (backsector->ceilingpic==skyflatnum) + (backsector->ceilingpic==skyflatnum) && + (backsector->ceilingheight<=backsector->floorheight) + ) + { + // e6y + // There is no more visual glitches with sky on Icarus map14 sector 187 + // Old code: wall.ybottom=(float)backsector->floorheight/MAP_SCALE; + wall.ybottom=((float)(backsector->floorheight + + (seg->sidedef->rowoffset > 0 ? seg->sidedef->rowoffset : 0)))/MAP_SCALE; + gld_AddSkyTexture(&wall, frontsector->sky, backsector->sky, SKY_CEILING); + } + else + { + if (bs->ceilingpic == skyflatnum && fs->ceilingpic != skyflatnum && + toptexture == NO_TEXTURE && midtexture == NO_TEXTURE) + { + wall.ybottom=(float)min_ceiling/MAP_SCALE; + if (bs->ceilingheight < fs->floorheight) + { + fix_sky_bleed = true; + } + gld_AddSkyTexture(&wall, frontsector->sky, backsector->sky, SKY_CEILING); + } + else + { + if (((backsector->ceilingpic != skyflatnum && toptexture != NO_TEXTURE) && midtexture == NO_TEXTURE) || + backsector->ceilingpic != skyflatnum || + backsector->ceilingheight <= frontsector->floorheight) + { + if (frontsector->ceilingpic == skyflatnum && frontsector->ceilingheight < backsector->floorheight) + { + wall.ybottom=(float)min_ceiling/MAP_SCALE; + fix_sky_bleed = true; + } + else + { + wall.ybottom=(float)max_ceiling/MAP_SCALE; + } + gld_AddSkyTexture(&wall, frontsector->sky, backsector->sky, SKY_CEILING); + } + } + } + } + if (floor_heightceilingpic==skyflatnum) && (backsector->ceilingpic==skyflatnum))) + { + temptex=gld_RegisterTexture(toptexture, true, false); + if (!temptex && gl_use_stencil && backsector && + !(seg->linedef->r_flags & RF_ISOLATED) && + /*frontsector->ceilingpic != skyflatnum && */backsector->ceilingpic != skyflatnum && + !(backsector->flags & NULL_SECTOR)) + { + wall.ytop=((float)(ceiling_height)/(float)MAP_SCALE)+SMALLDELTA; + wall.ybottom=((float)(floor_height)/(float)MAP_SCALE)-SMALLDELTA; + if (wall.ybottom >= zCamera) + { + wall.flag=GLDWF_TOPFLUD; + temptex=gld_RegisterFlat(flattranslation[seg->backsector->ceilingpic], true); + if (temptex) + { + wall.gltexture=temptex; + gld_AddDrawWallItem(GLDIT_FWALL, &wall); + } + } + } + else + if (temptex) + { + wall.gltexture=temptex; + CALC_Y_VALUES(wall, lineheight, floor_height, ceiling_height); + CALC_TEX_VALUES_TOP( + wall, seg, backseg, (LINE->flags & (/*e6y ML_DONTPEGBOTTOM | */ML_DONTPEGTOP))==0, + linelength, lineheight + ); + gld_AddDrawWallItem(GLDIT_WALL, &wall); + } + } + } + + /* midtexture */ + //e6y + if (comp[comp_maskedanim]) + temptex=gld_RegisterTexture(seg->sidedef->midtexture, true, false); + else + + // e6y + // Animated middle textures with a zero index should be forced + // See spacelab.wad (http://www.doomworld.com/idgames/index.php?id=6826) + temptex=gld_RegisterTexture(midtexture, true, true); + if (temptex && seg->sidedef->midtexture != NO_TEXTURE && backsector->ceilingheight>frontsector->floorheight) + { + int top, bottom; + wall.gltexture=temptex; + + if ( (LINE->flags & ML_DONTPEGBOTTOM) >0) + { + //floor_height=max_floor_tex; + floor_height=MAX(seg->frontsector->floorheight, seg->backsector->floorheight)+(seg->sidedef->rowoffset); + ceiling_height=floor_height+(wall.gltexture->realtexheight<frontsector->ceilingheight, seg->backsector->ceilingheight)+(seg->sidedef->rowoffset); + floor_height=ceiling_height-(wall.gltexture->realtexheight<frontsector != seg->backsector || + seg->frontsector->heightsec != -1) + { + sector_t *f, *b; + + f = (seg->frontsector->heightsec == -1 ? seg->frontsector : &ftempsec); + b = (seg->backsector->heightsec == -1 ? seg->backsector : &btempsec); + + // Set up the top + if (frontsector->ceilingpic != skyflatnum || + backsector->ceilingpic != skyflatnum) + { + if (toptexture == NO_TEXTURE) + // texture is missing - use the higher plane + top = MAX(f->ceilingheight, b->ceilingheight); + else + top = MIN(f->ceilingheight, b->ceilingheight); + } + else + top = ceiling_height; + + // Set up the bottom + if (frontsector->floorpic != skyflatnum || + backsector->floorpic != skyflatnum || + frontsector->floorheight != backsector->floorheight) + { + if (seg->sidedef->bottomtexture == NO_TEXTURE) + // texture is missing - use the lower plane + bottom = MIN(f->floorheight, b->floorheight); + else + // normal case - use the higher plane + bottom = MAX(f->floorheight, b->floorheight); + } + else + { + bottom = floor_height; + } + + //let's clip away some unnecessary parts of the polygon + if (ceiling_height < top) + top = ceiling_height; + if (floor_height > bottom) + bottom = floor_height; + } + else + { + // both sides of the line are in the same sector + top = ceiling_height; + bottom = floor_height; + } + + if (top <= bottom) + goto bottomtexture; + + wall.ytop = (float)top/(float)MAP_SCALE; + wall.ybottom = (float)bottom/(float)MAP_SCALE; + + wall.flag = GLDWF_M2S; + URUL(wall, seg, backseg, linelength); + + wall.vt = (float)((-top + ceiling_height))/(float)wall.gltexture->realtexheight; + wall.vb = (float)((-bottom + ceiling_height))/(float)wall.gltexture->realtexheight; + + /* Adjust the final float value accounting for the fixed point conversion */ + wall.vt /= FRACUNIT; + wall.vb /= FRACUNIT; + + if (seg->linedef->tranlump >= 0 && general_translucency) + wall.alpha=(float)tran_filter_pct/100.0f; + gld_AddDrawWallItem((wall.alpha == 1.0f ? GLDIT_MWALL : GLDIT_TWALL), &wall); + wall.alpha=1.0f; + } +bottomtexture: + /* bottomtexture */ + ceiling_height=backsector->floorheight; + floor_height=frontsector->floorheight; + if (frontsector->floorpic==skyflatnum) + { + wall.ybottom=-MAXCOORD; + if ( + (backsector->ceilingheight==backsector->floorheight) && + (backsector->floorpic==skyflatnum) + ) + { + wall.ytop=(float)backsector->floorheight/MAP_SCALE; + gld_AddSkyTexture(&wall, frontsector->sky, backsector->sky, SKY_FLOOR); + } + else + { + if (bottomtexture == NO_TEXTURE && midtexture == NO_TEXTURE) + { + wall.ytop=(float)max_floor/MAP_SCALE; + gld_AddSkyTexture(&wall, frontsector->sky, backsector->sky, SKY_CEILING); + } + else + { + if ((bottomtexture != NO_TEXTURE && midtexture == NO_TEXTURE) || + backsector->floorpic != skyflatnum || + backsector->floorheight >= frontsector->ceilingheight) + { + wall.ytop=(float)min_floor/MAP_SCALE; + gld_AddSkyTexture(&wall, frontsector->sky, backsector->sky, SKY_FLOOR); + } + } + } + } + if (floor_heightlinedef->r_flags & RF_ISOLATED) && + /*frontsector->floorpic != skyflatnum && */backsector->floorpic != skyflatnum && + !(backsector->flags & NULL_SECTOR)) + { + wall.ytop=((float)(ceiling_height)/(float)MAP_SCALE)+SMALLDELTA; + wall.ybottom=((float)(floor_height)/(float)MAP_SCALE)-SMALLDELTA; + if (wall.ytop <= zCamera) + { + wall.flag = GLDWF_BOTFLUD; + temptex=gld_RegisterFlat(flattranslation[seg->backsector->floorpic], true); + if (temptex) + { + wall.gltexture=temptex; + gld_AddDrawWallItem(GLDIT_FWALL, &wall); + } + } + } + else + if (temptex) + { + wall.gltexture=temptex; + fixed_t rowoffset = seg->sidedef->rowoffset; + if (fix_sky_bleed) + { + ceiling_height = MIN(frontsector->ceilingheight, backsector->ceilingheight); + seg->sidedef->rowoffset += (MAX(frontsector->floorheight, backsector->floorheight) - min_ceiling); + } + CALC_Y_VALUES(wall, lineheight, floor_height, ceiling_height); + CALC_TEX_VALUES_BOTTOM( + wall, seg, backseg, (LINE->flags & ML_DONTPEGBOTTOM)>0, + linelength, lineheight, + floor_height-frontsector->ceilingheight + ); + gld_AddDrawWallItem(GLDIT_WALL, &wall); + seg->sidedef->rowoffset = rowoffset; + } + } + } +} + +#undef LINE +#undef CALC_Y_VALUES +#undef OU +#undef OV +#undef OV_PEG +#undef CALC_TEX_VALUES_TOP +#undef CALC_TEX_VALUES_MIDDLE1S +#undef CALC_TEX_VALUES_BOTTOM +#undef ADDWALL + +/***************** + * * + * Flats * + * * + *****************/ + +static void gld_DrawFlat(GLFlat *flat) +{ + int loopnum; // current loop number + GLLoopDef *currentloop; // the current loop + dboolean has_detail; + int has_offset; + unsigned int flags; + + rendered_visplanes++; + + has_detail = + scene_has_details && + gl_arb_multitexture && + flat->gltexture->detail; + + has_offset = (has_detail || (flat->flags & GLFLAT_HAVE_OFFSET)); + + if ((sectorloops[flat->sectornum].flags & SECTOR_CLAMPXY) && (!has_detail) && + ((tex_filter[MIP_TEXTURE].mag_filter == GL_NEAREST) || + (flat->gltexture->flags & GLTEXTURE_HIRES)) && + !(flat->flags & GLFLAT_HAVE_OFFSET)) + flags = GLTEXTURE_CLAMPXY; + else + flags = 0; + + gld_BindFlat(flat->gltexture, flags); + gld_StaticLightAlpha(flat->light, flat->alpha); + +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glTranslatef(0.0f,flat->z,0.0f); +#endif + + if (has_offset) + { + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glTranslatef(flat->uoffs, flat->voffs, 0.0f); + } + + gld_BindDetailARB(flat->gltexture, has_detail); + if (has_detail) + { + float w, h, dx, dy; + detail_t *detail = flat->gltexture->detail; + + GLEXT_glActiveTextureARB(GL_TEXTURE1_ARB); + gld_StaticLightAlpha(flat->light, flat->alpha); + + glPushMatrix(); + + w = flat->gltexture->detail_width; + h = flat->gltexture->detail_height; + dx = detail->offsetx; + dy = detail->offsety; + + if ((flat->flags & GLFLAT_HAVE_OFFSET) || dx || dy) + { + glTranslatef(flat->uoffs * w + dx, flat->voffs * h + dy, 0.0f); + } + + glScalef(w, h, 1.0f); + } + + if (flat->sectornum>=0) + { + // go through all loops of this sector +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + if (gl_use_display_lists) + { + int display_list = (has_detail ? flats_detail_display_list : flats_display_list); + glCallList(display_list + flat->sectornum); + } + else + { + for (loopnum=0; loopnumsectornum].loopcount; loopnum++) + { + // set the current loop + currentloop=§orloops[flat->sectornum].loops[loopnum]; + glDrawArrays(currentloop->mode,currentloop->vertexindex,currentloop->vertexcount); + } + } +#else + for (loopnum=0; loopnumsectornum].loopcount; loopnum++) + { + int vertexnum; + // set the current loop + currentloop=§orloops[flat->sectornum].loops[loopnum]; + if (!currentloop) + continue; + // set the mode (GL_TRIANGLES, GL_TRIANGLE_STRIP or GL_TRIANGLE_FAN) + glBegin(currentloop->mode); + // go through all vertexes of this loop + for (vertexnum=currentloop->vertexindex; vertexnum<(currentloop->vertexindex+currentloop->vertexcount); vertexnum++) + { + // set texture coordinate of this vertex + if (has_detail) + { + GLEXT_glMultiTexCoord2fvARB(GL_TEXTURE0_ARB, (GLfloat*)&flats_vbo[vertexnum].u); + GLEXT_glMultiTexCoord2fvARB(GL_TEXTURE1_ARB, (GLfloat*)&flats_vbo[vertexnum].u); + } + else + { + glTexCoord2fv((GLfloat*)&flats_vbo[vertexnum].u); + } + // set vertex coordinate + //glVertex3fv((GLfloat*)&flats_vbo[vertexnum].x); + glVertex3f(flats_vbo[vertexnum].x, flat->z, flats_vbo[vertexnum].z); + } + // end of loop + glEnd(); + } +#endif + } + + //e6y + if (has_detail) + { + glPopMatrix(); + GLEXT_glActiveTextureARB(GL_TEXTURE0_ARB); + } + + if (has_offset) + { + glPopMatrix(); + } + +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +#endif +} + +// gld_AddFlat +// +// This draws on flat for the sector "num" +// The ceiling boolean indicates if the flat is a floor(false) or a ceiling(true) + +static void gld_AddFlat(int sectornum, dboolean ceiling, visplane_t *plane) +{ + sector_t *sector; // the sector we want to draw + sector_t tempsec; // needed for R_FakeFlat + int floorlightlevel; // killough 3/16/98: set floor lightlevel + int ceilinglightlevel; // killough 4/11/98 + GLFlat flat; + + if (sectornum<0) + return; + flat.sectornum=sectornum; + sector=§ors[sectornum]; // get the sector + sector=R_FakeFlat(sector, &tempsec, &floorlightlevel, &ceilinglightlevel, false); // for boom effects + flat.flags = (ceiling ? GLFLAT_CEILING : 0); + + if (!ceiling) // if it is a floor ... + { + if (sector->floorpic == skyflatnum) // don't draw if sky + return; + // get the texture. flattranslation is maintained by doom and + // contains the number of the current animation frame + flat.gltexture=gld_RegisterFlat(flattranslation[plane->picnum], true); + if (!flat.gltexture) + return; + // get the lightlevel from floorlightlevel + flat.light=gld_CalcLightLevel(plane->lightlevel+(extralight<<5)); + flat.fogdensity = gld_CalcFogDensity(sector, plane->lightlevel, GLDIT_FLOOR); + // calculate texture offsets + if (sector->floor_xoffs | sector->floor_yoffs) + { + flat.flags |= GLFLAT_HAVE_OFFSET; + flat.uoffs=(float)sector->floor_xoffs/(float)(FRACUNIT*64); + flat.voffs=(float)sector->floor_yoffs/(float)(FRACUNIT*64); + } + else + { + flat.uoffs=0.0f; + flat.voffs=0.0f; + } + } + else // if it is a ceiling ... + { + if (sector->ceilingpic == skyflatnum) // don't draw if sky + return; + // get the texture. flattranslation is maintained by doom and + // contains the number of the current animation frame + flat.gltexture=gld_RegisterFlat(flattranslation[plane->picnum], true); + if (!flat.gltexture) + return; + // get the lightlevel from ceilinglightlevel + flat.light=gld_CalcLightLevel(plane->lightlevel+(extralight<<5)); + flat.fogdensity = gld_CalcFogDensity(sector, plane->lightlevel, GLDIT_CEILING); + // calculate texture offsets + if (sector->ceiling_xoffs | sector->ceiling_yoffs) + { + flat.flags |= GLFLAT_HAVE_OFFSET; + flat.uoffs=(float)sector->ceiling_xoffs/(float)(FRACUNIT*64); + flat.voffs=(float)sector->ceiling_yoffs/(float)(FRACUNIT*64); + } + else + { + flat.uoffs=0.0f; + flat.voffs=0.0f; + } + } + + // get height from plane + flat.z=(float)plane->height/MAP_SCALE; + + if (gl_blend_animations) + { + anim_t *anim = anim_flats[flat.gltexture->index - firstflat].anim; + if (anim) + { + int currpic, nextpic; + + flat.alpha = 1.0f - ((float)tic_vars.frac + ((leveltime - 1) % anim->speed) * 65536.0f) / (65536.0f * anim->speed); + gld_AddDrawItem(((flat.flags & GLFLAT_CEILING) ? GLDIT_ACEILING : GLDIT_AFLOOR), &flat); + + currpic = flat.gltexture->index - firstflat - anim->basepic; + nextpic = anim->basepic + (currpic + 1) % anim->numpics; + flat.gltexture = gld_RegisterFlat(nextpic, true); + } + } + + flat.alpha = 1.0; + + if (flat.gltexture->detail) + scene_has_flat_details++; + + gld_AddDrawItem(((flat.flags & GLFLAT_CEILING) ? GLDIT_CEILING : GLDIT_FLOOR), &flat); +} + +void gld_AddPlane(int subsectornum, visplane_t *floor, visplane_t *ceiling) +{ + subsector_t *subsector; + + subsector = &subsectors[subsectornum]; + if (!subsector) + return; + + // render the floor + if (floor && floor->height < viewz) + gld_AddFlat(subsector->sector->iSectorID, false, floor); + // render the ceiling + if (ceiling && ceiling->height > viewz) + gld_AddFlat(subsector->sector->iSectorID, true, ceiling); +} + +/***************** + * * + * Sprites * + * * + *****************/ + +static void gld_DrawSprite(GLSprite *sprite) +{ + GLint blend_src, blend_dst; + int restore = 0; + + rendered_vissprites++; + + gld_BindPatch(sprite->gltexture,sprite->cm); + + if (!(sprite->flags & MF_NO_DEPTH_TEST)) + { + if(sprite->flags & MF_SHADOW) + { + glGetIntegerv(GL_BLEND_SRC, &blend_src); + glGetIntegerv(GL_BLEND_DST, &blend_dst); + glBlendFunc(gl_fuzzsfactors[gl_thingspritefuzzmode], + gl_fuzzdfactors[gl_thingspritefuzzmode]); + //glColor4f(0.2f,0.2f,0.2f,(float)tran_filter_pct/100.0f); + glAlphaFunc(GL_GEQUAL,0.1f); + glColor4f(0.2f,0.2f,0.2f,0.33f); + restore = 1; + } + else + { + if(sprite->flags & MF_TRANSLUCENT) + gld_StaticLightAlpha(sprite->light,(float)tran_filter_pct/100.0f); + else + gld_StaticLight(sprite->light); + } + } + + if (!render_paperitems && !(sprite->flags & (MF_SOLID | MF_SPAWNCEILING))) + { + float x1, x2, x3, x4, z1, z2, z3, z4; + float y1, y2, cy, ycenter, y1c, y2c; + float y1z2_y, y2z2_y; + + ycenter = (float)fabs(sprite->y1 - sprite->y2) * 0.5f; + y1c = sprite->y1 - ycenter; + y2c = sprite->y2 - ycenter; + cy = sprite->y + ycenter; + + y1z2_y = -(y1c * sin_paperitems_pitch); + y2z2_y = -(y2c * sin_paperitems_pitch); + + x1 = +(sprite->x1 * cos_inv_yaw - y1z2_y * sin_inv_yaw) + sprite->x; + x2 = +(sprite->x2 * cos_inv_yaw - y1z2_y * sin_inv_yaw) + sprite->x; + x3 = +(sprite->x1 * cos_inv_yaw - y2z2_y * sin_inv_yaw) + sprite->x; + x4 = +(sprite->x2 * cos_inv_yaw - y2z2_y * sin_inv_yaw) + sprite->x; + + y1 = +(y1c * cos_paperitems_pitch) + cy; + y2 = +(y2c * cos_paperitems_pitch) + cy; + + z1 = -(sprite->x1 * sin_inv_yaw + y1z2_y * cos_inv_yaw) + sprite->z; + z2 = -(sprite->x2 * sin_inv_yaw + y1z2_y * cos_inv_yaw) + sprite->z; + z3 = -(sprite->x1 * sin_inv_yaw + y2z2_y * cos_inv_yaw) + sprite->z; + z4 = -(sprite->x2 * sin_inv_yaw + y2z2_y * cos_inv_yaw) + sprite->z; + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(sprite->ul, sprite->vt); glVertex3f(x1, y1, z1); + glTexCoord2f(sprite->ur, sprite->vt); glVertex3f(x2, y1, z2); + glTexCoord2f(sprite->ul, sprite->vb); glVertex3f(x3, y2, z3); + glTexCoord2f(sprite->ur, sprite->vb); glVertex3f(x4, y2, z4); + glEnd(); + } + else + { + float x1, x2, y1, y2, z1, z2; + + x1 = +(sprite->x1 * cos_inv_yaw) + sprite->x; + x2 = +(sprite->x2 * cos_inv_yaw) + sprite->x; + + y1 = sprite->y + sprite->y1; + y2 = sprite->y + sprite->y2; + + z2 = -(sprite->x1 * sin_inv_yaw) + sprite->z; + z1 = -(sprite->x2 * sin_inv_yaw) + sprite->z; + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(sprite->ul, sprite->vt); glVertex3f(x1, y1, z2); + glTexCoord2f(sprite->ur, sprite->vt); glVertex3f(x2, y1, z1); + glTexCoord2f(sprite->ul, sprite->vb); glVertex3f(x1, y2, z2); + glTexCoord2f(sprite->ur, sprite->vb); glVertex3f(x2, y2, z1); + glEnd(); + } + + if (restore) + { + glBlendFunc(blend_src, blend_dst); + glAlphaFunc(GL_GEQUAL, gl_mask_sprite_threshold_f); + } +} + +static void gld_AddHealthBar(mobj_t* thing, GLSprite *sprite) +{ + if (((thing->flags & (MF_COUNTKILL | MF_CORPSE)) == MF_COUNTKILL) && (thing->health > 0)) + { + GLHealthBar hbar; + int health_percent = thing->health * 100 / thing->info->spawnhealth; + + hbar.cm = -1; + if (health_percent <= health_bar_red) + hbar.cm = CR_RED; + else if (health_percent <= health_bar_yellow) + hbar.cm = CR_YELLOW; + else if (health_percent <= health_bar_green) + hbar.cm = CR_GREEN; + + if (hbar.cm >= 0) + { + float sx2 = (float)thing->radius / 2.0f / MAP_SCALE; + float sx1 = sx2 - (float)health_percent * (float)thing->radius / 100.0f / MAP_SCALE; + float sx3 = -sx2; + + hbar.x1 = +(sx1 * cos_inv_yaw) + sprite->x; + hbar.x2 = +(sx2 * cos_inv_yaw) + sprite->x; + hbar.x3 = +(sx3 * cos_inv_yaw) + sprite->x; + + hbar.z1 = -(sx1 * sin_inv_yaw) + sprite->z; + hbar.z2 = -(sx2 * sin_inv_yaw) + sprite->z; + hbar.z3 = -(sx3 * sin_inv_yaw) + sprite->z; + + hbar.y = sprite->y + sprite->y1 + 2.0f / MAP_COEFF; + + gld_AddDrawItem(GLDIT_HBAR, &hbar); + } + } +} + +static void gld_DrawHealthBars(void) +{ + int i, count; + int cm = -1; + + count = gld_drawinfo.num_items[GLDIT_HBAR]; + if (count > 0) + { + gld_EnableTexture2D(GL_TEXTURE0_ARB, false); + + glBegin(GL_LINES); + for (i = count - 1; i >= 0; i--) + { + GLHealthBar *hbar = gld_drawinfo.items[GLDIT_HBAR][i].item.hbar; + if (hbar->cm != cm) + { + cm = hbar->cm; + glColor4f(cm2RGB[cm][0], cm2RGB[cm][1], cm2RGB[cm][2], 1.0f); + } + + glVertex3f(hbar->x1, hbar->y, hbar->z1); + glVertex3f(hbar->x2, hbar->y, hbar->z2); + } + glEnd(); + + if (health_bar_full_length) + { + glColor4f(0.5f, 0.5f, 0.5f, 1.0f); + glBegin(GL_LINES); + for (i = count - 1; i >= 0; i--) + { + GLHealthBar *hbar = gld_drawinfo.items[GLDIT_HBAR][i].item.hbar; + + glVertex3f(hbar->x1, hbar->y, hbar->z1); + glVertex3f(hbar->x3, hbar->y, hbar->z3); + } + glEnd(); + } + + gld_EnableTexture2D(GL_TEXTURE0_ARB, true); + } +} + +void gld_ProjectSprite(mobj_t* thing, int lightlevel) +{ + fixed_t tx; + spritedef_t *sprdef; + spriteframe_t *sprframe; + int lump; + dboolean flip; + int heightsec; // killough 3/27/98 + + // transform the origin point + //e6y + fixed_t tr_x, tr_y; + fixed_t fx, fy, fz; + fixed_t gxt, gyt; + fixed_t tz; + + GLSprite sprite; + const rpatch_t* patch; + + int frustum_culling = HaveMouseLook() && gl_sprites_frustum_culling; + int mlook = HaveMouseLook() || (render_fov > FOV90); + + if (!paused && movement_smooth) + { + fx = thing->PrevX + FixedMul (tic_vars.frac, thing->x - thing->PrevX); + fy = thing->PrevY + FixedMul (tic_vars.frac, thing->y - thing->PrevY); + fz = thing->PrevZ + FixedMul (tic_vars.frac, thing->z - thing->PrevZ); + } + else + { + fx = thing->x; + fy = thing->y; + fz = thing->z; + } + + tr_x = fx - viewx; + tr_y = fy - viewy; + + gxt = FixedMul(tr_x, viewcos); + gyt = -FixedMul(tr_y, viewsin); + + tz = gxt - gyt; + + // thing is behind view plane? + if (tz < r_near_clip_plane) + return; + + gxt = -FixedMul(tr_x, viewsin); + gyt = FixedMul(tr_y, viewcos); + tx = -(gyt + gxt); + + //e6y + if (!render_paperitems && mlook) + { + if (tz >= MINZ && (D_abs(tx) >> 5) > tz) + return; + } + else + { + // too far off the side? + if (D_abs(tx) > (tz << 2)) + return; + } + + // decide which patch to use for sprite relative to player +#ifdef RANGECHECK + if ((unsigned) thing->sprite >= (unsigned)numsprites) + I_Error ("R_ProjectSprite: Invalid sprite number %i", thing->sprite); +#endif + + sprdef = &sprites[thing->sprite]; + +#ifdef RANGECHECK + if ((thing->frame&FF_FRAMEMASK) >= sprdef->numframes) + I_Error ("R_ProjectSprite: Invalid sprite frame %i : %i", thing->sprite, thing->frame); +#endif + + if (!sprdef->spriteframes) + I_Error("R_ProjectSprite: Missing spriteframes %i : %i", thing->sprite, thing->frame); + + sprframe = &sprdef->spriteframes[thing->frame & FF_FRAMEMASK]; + + if (sprframe->rotate) + { + // choose a different rotation based on player view + angle_t rot; + angle_t ang = R_PointToAngle2(viewx, viewy, fx, fy); + if (sprframe->lump[0] == sprframe->lump[1]) + { + rot = (ang - thing->angle + (angle_t)(ANG45/2)*9) >> 28; + } + else + { + rot = (ang - thing->angle + (angle_t)(ANG45 / 2) * 9 - + (angle_t)(ANG180 / 16)) >> 28; + } + lump = sprframe->lump[rot]; + flip = (dboolean)(sprframe->flip & (1 << rot)); + } + else + { + // use single rotation for all views + lump = sprframe->lump[0]; + flip = (dboolean)(sprframe->flip & 1); + } + lump += firstspritelump; + + patch = R_CachePatchNum(lump); + thing->patch_width = patch->width; + + // killough 4/9/98: clip things which are out of view due to height + if(!mlook) + { + int x1, x2; + fixed_t xscale = FixedDiv(projection, tz); + /* calculate edges of the shape + * cph 2003/08/1 - fraggle points out that this offset must be flipped + * if the sprite is flipped; e.g. FreeDoom imp is messed up by this. */ + if (flip) + tx -= (patch->width - patch->leftoffset) << FRACBITS; + else + tx -= patch->leftoffset << FRACBITS; + + x1 = (centerxfrac + FixedMul(tx, xscale)) >> FRACBITS; + tx += patch->width << FRACBITS; + x2 = ((centerxfrac + FixedMul (tx, xscale) - FRACUNIT/2) >> FRACBITS); + + // off the side? + if (x1 > viewwidth || x2 < 0) + goto unlock_patch; + } + + // killough 3/27/98: exclude things totally separated + // from the viewer, by either water or fake ceilings + // killough 4/11/98: improve sprite clipping for underwater/fake ceilings + + heightsec = thing->subsector->sector->heightsec; + if (heightsec != -1) // only clip things which are in special sectors + { + int phs = viewplayer->mo->subsector->sector->heightsec; + fixed_t gzt = fz + (patch->topoffset << FRACBITS); + if (phs != -1 && viewz < sectors[phs].floorheight ? + fz >= sectors[heightsec].floorheight : + gzt < sectors[heightsec].floorheight) + goto unlock_patch; + if (phs != -1 && viewz > sectors[phs].ceilingheight ? + gzt < sectors[heightsec].ceilingheight && viewz >= sectors[heightsec].ceilingheight : + fz >= sectors[heightsec].ceilingheight) + goto unlock_patch; + } + + //e6y FIXME!!! + if (thing == players[displayplayer].mo && walkcamera.type != 2) + goto unlock_patch; + + sprite.x =-(float)fx / MAP_SCALE; + sprite.y = (float)fz / MAP_SCALE; + sprite.z = (float)fy / MAP_SCALE; + + // Bring items up out of floor by configurable amount times .01 Mead 8/13/03 + sprite.y += gl_sprite_offset; + + sprite.x2 = (float)patch->leftoffset / MAP_COEFF; + sprite.x1 = sprite.x2 - ((float)patch->width / MAP_COEFF); + sprite.y1 = (float)patch->topoffset / MAP_COEFF; + sprite.y2 = sprite.y1 - ((float)patch->height / MAP_COEFF); + + // e6y + // if the sprite is below the floor, and it's not a hanger/floater/missile, + // and it's not a fully dead corpse, move it up + if ((gl_spriteclip != spriteclip_const) && + (sprite.y2 < 0) && (sprite.y2 >= (float)(-gl_spriteclip_threshold_f)) && + !(thing->flags & (MF_SPAWNCEILING|MF_FLOAT|MF_MISSILE|MF_NOGRAVITY)) && + ((gl_spriteclip == spriteclip_always) || !((thing->flags & MF_CORPSE) && thing->tics == -1))) + { + sprite.y1 -= sprite.y2; + sprite.y2 = 0.0f; + } + + if (frustum_culling) + { + if (!gld_SphereInFrustum( + sprite.x + cos_inv_yaw * (sprite.x1 + sprite.x2) / 2.0f, + sprite.y + (sprite.y1 + sprite.y2) / 2.0f, + sprite.z - sin_inv_yaw * (sprite.x1 + sprite.x2) / 2.0f, + //1.5 == sqrt(2) + small delta for MF_FOREGROUND + (float)(MAX(patch->width, patch->height)) / MAP_COEFF / 2.0f * 1.5f)) + { + goto unlock_patch; + } + } + + sprite.scale = FixedDiv(projectiony, tz);; + if ((thing->frame & FF_FULLBRIGHT) || show_alive) + { + sprite.fogdensity = 0.0f; + sprite.light = 1.0f; + } + else + { + sprite.light = gld_CalcLightLevel(lightlevel+(extralight<<5)); + sprite.fogdensity = gld_CalcFogDensity(thing->subsector->sector, lightlevel, GLDIT_SPRITE); + } + sprite.cm = CR_LIMIT + (int)((thing->flags & MF_TRANSLATION) >> (MF_TRANSSHIFT)); + // [FG] colored blood and gibs + if (thing->flags & MF_COLOREDBLOOD) + { + sprite.cm = thing->bloodcolor; + } + sprite.gltexture = gld_RegisterPatch(lump, sprite.cm, true); + if (!sprite.gltexture) + goto unlock_patch; + sprite.flags = thing->flags; + + if (thing->flags & MF_FOREGROUND) + scene_has_overlapped_sprites = true; + + sprite.index = gl_spriteindex++; + sprite.xy = thing->x + (thing->y >> 16); + sprite.fx = thing->x; + sprite.fy = thing->y; + + sprite.vt = 0.0f; + sprite.vb = sprite.gltexture->scaleyfac; + if (flip) + { + sprite.ul = 0.0f; + sprite.ur = sprite.gltexture->scalexfac; + } + else + { + sprite.ul = sprite.gltexture->scalexfac; + sprite.ur = 0.0f; + } + + //e6y: support for transparent sprites + if (sprite.flags & MF_NO_DEPTH_TEST) + { + gld_AddDrawItem(GLDIT_ASPRITE, &sprite); + } + else + { + gld_AddDrawItem((gl_sprite_blend || (sprite.flags & (MF_SHADOW | MF_TRANSLUCENT)) ? GLDIT_TSPRITE : GLDIT_SPRITE), &sprite); + gld_ProcessThingShadow(thing); + } + + if (health_bar) + { + gld_AddHealthBar(thing, &sprite); + } + +unlock_patch: + R_UnlockPatchNum(lump); +} + +/***************** + * * + * Draw * + * * + *****************/ + +//e6y +void gld_ProcessWall(GLWall *wall) +{ + // e6y + // The ultimate 'ATI sucks' fix: Some of ATIs graphics cards are so unprecise when + // rendering geometry that each and every border between polygons must be seamless, + // otherwise there are rendering artifacts worse than anything that could be seen + // on Geforce 2's! Made this a menu option because the speed impact is quite severe + // and this special handling is not necessary on modern NVidia cards. + seg_t *seg = wall->seg; + + wall->glseg->fracleft = 0; + wall->glseg->fracright = 0; + + gld_RecalcVertexHeights(seg->linedef->v1); + gld_RecalcVertexHeights(seg->linedef->v2); + + gld_DrawWall(wall); +} + +static int C_DECL dicmp_wall(const void *a, const void *b) +{ + GLTexture *tx1 = ((const GLDrawItem *)a)->item.wall->gltexture; + GLTexture *tx2 = ((const GLDrawItem *)b)->item.wall->gltexture; + return tx1 - tx2; +} +static int C_DECL dicmp_flat(const void *a, const void *b) +{ + GLTexture *tx1 = ((const GLDrawItem *)a)->item.flat->gltexture; + GLTexture *tx2 = ((const GLDrawItem *)b)->item.flat->gltexture; + return tx1 - tx2; +} +static int C_DECL dicmp_sprite(const void *a, const void *b) +{ + GLTexture *tx1 = ((const GLDrawItem *)a)->item.sprite->gltexture; + GLTexture *tx2 = ((const GLDrawItem *)b)->item.sprite->gltexture; + return tx1 - tx2; +} + +static int C_DECL dicmp_sprite_scale(const void *a, const void *b) +{ + GLSprite *sprite1 = ((const GLDrawItem *)a)->item.sprite; + GLSprite *sprite2 = ((const GLDrawItem *)b)->item.sprite; + + if (sprite1->scale != sprite2->scale) + { + return sprite2->scale - sprite1->scale; + } + else + { + return sprite1->gltexture - sprite2->gltexture; + } +} + +static void gld_DrawItemsSortByTexture(GLDrawItemType itemtype) +{ + typedef int(C_DECL *DICMP_ITEM)(const void *a, const void *b); + + static DICMP_ITEM itemfuncs[GLDIT_TYPES] = { + 0, + dicmp_wall, dicmp_wall, dicmp_wall, dicmp_wall, dicmp_wall, + dicmp_wall, dicmp_wall, + dicmp_flat, dicmp_flat, + dicmp_flat, dicmp_flat, + dicmp_sprite, dicmp_sprite_scale, dicmp_sprite, + 0, + 0, + }; + + if (itemfuncs[itemtype] && gld_drawinfo.num_items[itemtype] > 1) + { + qsort(gld_drawinfo.items[itemtype], gld_drawinfo.num_items[itemtype], + sizeof(gld_drawinfo.items[itemtype][0]), itemfuncs[itemtype]); + } +} + +static int no_overlapped_sprites; +static int C_DECL dicmp_sprite_by_pos(const void *a, const void *b) +{ + GLSprite *s1 = ((const GLDrawItem *)a)->item.sprite; + GLSprite *s2 = ((const GLDrawItem *)b)->item.sprite; + int res = s2->xy - s1->xy; + no_overlapped_sprites = no_overlapped_sprites && res; + return res; +} + +static void gld_DrawItemsSort(GLDrawItemType itemtype, int (C_DECL *PtFuncCompare)(const void *, const void *)) +{ + qsort(gld_drawinfo.items[itemtype], gld_drawinfo.num_items[itemtype], + sizeof(gld_drawinfo.items[itemtype][0]), PtFuncCompare); +} + +static void gld_DrawItemsSortSprites(GLDrawItemType itemtype) +{ + static const float delta = 0.2f / MAP_COEFF; + int i; + + if (scene_has_overlapped_sprites && sprites_doom_order == DOOM_ORDER_STATIC) + { + for (i = 0; i < gld_drawinfo.num_items[itemtype]; i++) + { + GLSprite *sprite = gld_drawinfo.items[itemtype][i].item.sprite; + if (sprite->flags & MF_FOREGROUND) + { + sprite->index = gl_spriteindex; + sprite->x -= delta * sin_inv_yaw; + sprite->z -= delta * cos_inv_yaw; + } + } + } + + if (sprites_doom_order == DOOM_ORDER_DYNAMIC) + { + no_overlapped_sprites = true; + gld_DrawItemsSort(itemtype, dicmp_sprite_by_pos); // back to front + + if (!no_overlapped_sprites) + { + // there are overlapped sprites + int count = gld_drawinfo.num_items[itemtype]; + + i = 1; + while (i < count) + { + GLSprite *sprite1 = gld_drawinfo.items[itemtype][i - 1].item.sprite; + GLSprite *sprite2 = gld_drawinfo.items[itemtype][i - 0].item.sprite; + + if (sprite1->xy == sprite2->xy) + { + GLSprite *sprite = (sprite1->index > sprite2->index ? sprite1 : sprite2); + i++; + while (i < count && gld_drawinfo.items[itemtype][i].item.sprite->xy == sprite1->xy) + { + if (gld_drawinfo.items[itemtype][i].item.sprite->index > sprite->index) + { + sprite = gld_drawinfo.items[itemtype][i].item.sprite; + } + i++; + } + + // 'nearest' + sprite->index = gl_spriteindex; + sprite->x -= delta * sin_inv_yaw; + sprite->z -= delta * cos_inv_yaw; + } + i++; + } + } + } + + gld_DrawItemsSortByTexture(itemtype); +} + +// +// projected walls +// +void gld_DrawProjectedWalls(GLDrawItemType itemtype) +{ + int i; + + if (gl_use_stencil && gld_drawinfo.num_items[itemtype] > 0) + { + // Push bleeding floor/ceiling textures back a little in the z-buffer + // so they don't interfere with overlapping mid textures. + glPolygonOffset(1.0f, 128.0f); + glEnable(GL_POLYGON_OFFSET_FILL); + + glEnable(GL_STENCIL_TEST); + gld_DrawItemsSortByTexture(itemtype); + for (i = gld_drawinfo.num_items[itemtype] - 1; i >= 0; i--) + { + GLWall *wall = gld_drawinfo.items[itemtype][i].item.wall; + + if (gl_use_fog) + { + // calculation of fog density for flooded walls + if (wall->seg->backsector) + { + wall->fogdensity = gld_CalcFogDensity(wall->seg->frontsector, + wall->seg->backsector->lightlevel, itemtype); + } + + gld_SetFog(wall->fogdensity); + } + + gld_ProcessWall(wall); + } + glDisable(GL_STENCIL_TEST); + + glPolygonOffset(0.0f, 0.0f); + glDisable(GL_POLYGON_OFFSET_FILL); + } +} + +void gld_InitDisplayLists(void) +{ + int i; + int loopnum; // current loop number + GLLoopDef *currentloop; + + if (gl_use_display_lists) + { + flats_display_list_size = numsectors; + flats_display_list = glGenLists(flats_display_list_size); + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + if (gl_ext_arb_vertex_buffer_object) + { + GLEXT_glBindBufferARB(GL_ARRAY_BUFFER, flats_vbo_id); + } + glVertexPointer(3, GL_FLOAT, sizeof(flats_vbo[0]), flats_vbo_x); + glTexCoordPointer(2, GL_FLOAT, sizeof(flats_vbo[0]), flats_vbo_u); + + for (i = 0; i < flats_display_list_size; i++) + { + glNewList(flats_display_list + i, GL_COMPILE); + + for (loopnum = 0; loopnum < sectorloops[i].loopcount; loopnum++) + { + // set the current loop + currentloop = §orloops[i].loops[loopnum]; + glDrawArrays(currentloop->mode, currentloop->vertexindex, currentloop->vertexcount); + } + + glEndList(); + } + + // duplicated display list for flats with enabled detail ARB + if (details_count && gl_arb_multitexture) + { + flats_detail_display_list_size = numsectors; + flats_detail_display_list = glGenLists(flats_detail_display_list_size); + + gld_EnableClientCoordArray(GL_TEXTURE1_ARB, true); + + for (i = 0; i < flats_display_list_size; i++) + { + glNewList(flats_detail_display_list + i, GL_COMPILE); + + for (loopnum = 0; loopnum < sectorloops[i].loopcount; loopnum++) + { + // set the current loop + currentloop = §orloops[i].loops[loopnum]; + glDrawArrays(currentloop->mode, currentloop->vertexindex, currentloop->vertexcount); + } + + glEndList(); + } + + gld_EnableClientCoordArray(GL_TEXTURE1_ARB, false); + } + + if (gl_ext_arb_vertex_buffer_object) + { + // bind with 0, so, switch back to normal pointer operation + GLEXT_glBindBufferARB(GL_ARRAY_BUFFER, 0); + } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + } +} + +void gld_CleanDisplayLists(void) +{ + if (gl_use_display_lists) + { + if (flats_display_list_size > 0) + { + glDeleteLists(flats_display_list, flats_display_list_size); + flats_display_list = 0; + flats_display_list_size = 0; + } + + if (flats_detail_display_list_size > 0) + { + glDeleteLists(flats_detail_display_list, flats_detail_display_list_size); + flats_detail_display_list = 0; + flats_detail_display_list_size = 0; + } + } +} + +void gld_DrawScene(player_t *player) +{ + int i; + int skybox; + + //e6y: must call it twice for correct initialisation + glEnable(GL_ALPHA_TEST); + + //e6y: the same with fog + gl_EnableFog(true); + gl_EnableFog(false); + + gld_EnableDetail(false); + gld_InitFrameDetails(); + +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + if (!gl_use_display_lists) + { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + } +#endif + + //e6y: skybox + skybox = 0; + if (gl_drawskys != skytype_none) + { + skybox = gld_DrawBoxSkyBox(); + } + + if (!skybox) + { + if (gl_drawskys == skytype_skydome) + { + gld_DrawDomeSkyBox(); + } + //e6y: 3d emulation of screen quad + if (gl_drawskys == skytype_screen) + { + gld_DrawScreenSkybox(); + } + } + +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + if (!gl_use_display_lists) + { + if (gl_ext_arb_vertex_buffer_object) + { + GLEXT_glBindBufferARB(GL_ARRAY_BUFFER, flats_vbo_id); + } + glVertexPointer(3, GL_FLOAT, sizeof(flats_vbo[0]), flats_vbo_x); + glTexCoordPointer(2, GL_FLOAT, sizeof(flats_vbo[0]), flats_vbo_u); + } +#endif + + glsl_SetActiveShader(sh_main); + + // + // opaque stuff + // + + glBlendFunc(GL_ONE, GL_ZERO); + + // solid geometry + glDisable(GL_ALPHA_TEST); + + // enable backside removing + glEnable(GL_CULL_FACE); + + // floors + glCullFace(GL_FRONT); + gld_DrawItemsSortByTexture(GLDIT_FLOOR); + for (i = gld_drawinfo.num_items[GLDIT_FLOOR] - 1; i >= 0; i--) + { + gld_SetFog(gld_drawinfo.items[GLDIT_FLOOR][i].item.flat->fogdensity); + gld_DrawFlat(gld_drawinfo.items[GLDIT_FLOOR][i].item.flat); + } + + // ceilings + glCullFace(GL_BACK); + gld_DrawItemsSortByTexture(GLDIT_CEILING); + for (i = gld_drawinfo.num_items[GLDIT_CEILING] - 1; i >= 0; i--) + { + gld_SetFog(gld_drawinfo.items[GLDIT_CEILING][i].item.flat->fogdensity); + gld_DrawFlat(gld_drawinfo.items[GLDIT_CEILING][i].item.flat); + } + + // disable backside removing + glDisable(GL_CULL_FACE); + + // detail texture works only with flats and walls + gld_EnableDetail(false); + + // top, bottom, one-sided walls + gld_DrawItemsSortByTexture(GLDIT_WALL); + for (i = gld_drawinfo.num_items[GLDIT_WALL] - 1; i >= 0; i--) + { + gld_SetFog(gld_drawinfo.items[GLDIT_WALL][i].item.wall->fogdensity); + gld_ProcessWall(gld_drawinfo.items[GLDIT_WALL][i].item.wall); + } + + // masked geometry + glEnable(GL_ALPHA_TEST); + + gld_DrawItemsSortByTexture(GLDIT_MWALL); + + if (!gl_arb_multitexture && render_usedetail && gl_use_stencil && + gld_drawinfo.num_items[GLDIT_MWALL] > 0) + { + // opaque mid walls without holes + for (i = gld_drawinfo.num_items[GLDIT_MWALL] - 1; i >= 0; i--) + { + GLWall *wall = gld_drawinfo.items[GLDIT_MWALL][i].item.wall; + if (!(wall->gltexture->flags & GLTEXTURE_HASHOLES)) + { + gld_SetFog(wall->fogdensity); + gld_ProcessWall(wall); + } + } + + // opaque mid walls with holes + + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 1, ~0); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + + for (i = gld_drawinfo.num_items[GLDIT_MWALL] - 1; i >= 0; i--) + { + GLWall *wall = gld_drawinfo.items[GLDIT_MWALL][i].item.wall; + if (wall->gltexture->flags & GLTEXTURE_HASHOLES) + { + gld_SetFog(wall->fogdensity); + gld_ProcessWall(wall); + } + } + + glStencilFunc(GL_EQUAL, 1, ~0); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); + glBlendFunc (GL_DST_COLOR, GL_SRC_COLOR); + + // details for opaque mid walls with holes + gld_DrawItemsSortByDetail(GLDIT_MWALL); + for (i = gld_drawinfo.num_items[GLDIT_MWALL] - 1; i >= 0; i--) + { + GLWall *wall = gld_drawinfo.items[GLDIT_MWALL][i].item.wall; + if (wall->gltexture->flags & GLTEXTURE_HASHOLES) + { + gld_SetFog(wall->fogdensity); + gld_DrawWallDetail_NoARB(wall); + } + } + + //restoring + SetFrameTextureMode(); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glClear(GL_STENCIL_BUFFER_BIT); + glDisable(GL_STENCIL_TEST); + } + else + { + // opaque mid walls + for (i = gld_drawinfo.num_items[GLDIT_MWALL] - 1; i >= 0; i--) + { + gld_SetFog(gld_drawinfo.items[GLDIT_MWALL][i].item.wall->fogdensity); + gld_ProcessWall(gld_drawinfo.items[GLDIT_MWALL][i].item.wall); + } + } + + gl_EnableFog(false); + gld_EnableDetail(false); + + // projected walls + gld_DrawProjectedWalls(GLDIT_FWALL); + + gl_EnableFog(false); + glEnable(GL_ALPHA_TEST); + + // normal sky (not a skybox) + if (!skybox && (gl_drawskys == skytype_none || gl_drawskys == skytype_standard)) + { + rendered_segs += gld_drawinfo.num_items[GLDIT_SWALL]; + // fake strips of sky + glsl_SetActiveShader(NULL); + gld_DrawStripsSky(); + glsl_SetActiveShader(sh_main); + } + + // opaque sprites + glAlphaFunc(GL_GEQUAL, gl_mask_sprite_threshold_f); + gld_DrawItemsSortSprites(GLDIT_SPRITE); + for (i = gld_drawinfo.num_items[GLDIT_SPRITE] - 1; i >= 0; i--) + { + gld_SetFog(gld_drawinfo.items[GLDIT_SPRITE][i].item.sprite->fogdensity); + gld_DrawSprite(gld_drawinfo.items[GLDIT_SPRITE][i].item.sprite); + } + glAlphaFunc(GL_GEQUAL, 0.5f); + + // mode for viewing all the alive monsters + if (show_alive) + { + const int period = 250; + float color; + int step = (SDL_GetTicks() % (period * 2)) + 1; + if (step > period) + { + step = period * 2 - step; + } + color = 0.1f + 0.9f * (float)step / (float)period; + + R_AddAllAliveMonstersSprites(); + glDisable(GL_DEPTH_TEST); + gld_DrawItemsSortByTexture(GLDIT_ASPRITE); + glColor4f(1.0f, color, color, 1.0f); + for (i = gld_drawinfo.num_items[GLDIT_ASPRITE] - 1; i >= 0; i--) + { + gld_DrawSprite(gld_drawinfo.items[GLDIT_ASPRITE][i].item.sprite); + } + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glEnable(GL_DEPTH_TEST); + } + + if (health_bar) + { + glsl_SetActiveShader(NULL); + gld_DrawHealthBars(); + glsl_SetActiveShader(sh_main); + } + + // + // transparent stuff + // + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if (gl_blend_animations) + { + // enable backside removing + glEnable(GL_CULL_FACE); + + // animated floors + glCullFace(GL_FRONT); + gld_DrawItemsSortByTexture(GLDIT_AFLOOR); + for (i = gld_drawinfo.num_items[GLDIT_AFLOOR] - 1; i >= 0; i--) + { + gld_SetFog(gld_drawinfo.items[GLDIT_AFLOOR][i].item.flat->fogdensity); + gld_DrawFlat(gld_drawinfo.items[GLDIT_AFLOOR][i].item.flat); + } + + glCullFace(GL_BACK); + gld_DrawItemsSortByTexture(GLDIT_ACEILING); + for (i = gld_drawinfo.num_items[GLDIT_ACEILING] - 1; i >= 0; i--) + { + gld_SetFog(gld_drawinfo.items[GLDIT_ACEILING][i].item.flat->fogdensity); + gld_DrawFlat(gld_drawinfo.items[GLDIT_ACEILING][i].item.flat); + } + + // disable backside removing + glDisable(GL_CULL_FACE); + } + + if (gl_blend_animations) + { + gld_DrawItemsSortByTexture(GLDIT_AWALL); + for (i = gld_drawinfo.num_items[GLDIT_AWALL] - 1; i >= 0; i--) + { + gld_SetFog(gld_drawinfo.items[GLDIT_AWALL][i].item.wall->fogdensity); + gld_ProcessWall(gld_drawinfo.items[GLDIT_AWALL][i].item.wall); + } + + // projected animated walls + gld_DrawProjectedWalls(GLDIT_FAWALL); + } + + glsl_SetActiveShader(NULL); + gld_RenderShadows(); + glsl_SetActiveShader(sh_main); + + /* Transparent sprites and transparent things must be rendered + * in far-to-near order. The approach used here is to sort in- + * place by comparing the next farthest items in the queue. + * There are known limitations to this approach, but it is + * a trade-off of accuracy for speed. + * Refer to the discussion below for more detail. + * https://github.com/coelckers/prboom-plus/pull/262 + */ + if (gld_drawinfo.num_items[GLDIT_TWALL] > 0 || gld_drawinfo.num_items[GLDIT_TSPRITE] > 0) + { + int twall_idx = gld_drawinfo.num_items[GLDIT_TWALL] - 1; + int tsprite_idx = gld_drawinfo.num_items[GLDIT_TSPRITE] - 1; + + if (tsprite_idx > 0) + gld_DrawItemsSortSprites(GLDIT_TSPRITE); + + while (twall_idx >= 0 || tsprite_idx >= 0 ) + { + dboolean draw_tsprite = false; + + /* find out what is next to draw */ + if (twall_idx >= 0 && tsprite_idx >= 0) + { + /* both are left to draw, determine + * which is farther */ + seg_t *twseg = gld_drawinfo.items[GLDIT_TWALL][twall_idx].item.wall->seg; + int ti; + for (ti = tsprite_idx; ti >= 0; ti--) { + /* reconstruct the sprite xy */ + fixed_t tsx = gld_drawinfo.items[GLDIT_TSPRITE][ti].item.sprite->fx; + fixed_t tsy = gld_drawinfo.items[GLDIT_TSPRITE][ti].item.sprite->fy; + + if (R_PointOnSegSide(tsx, tsy, twseg)) + { + /* a thing is behind the seg */ + /* do not draw the seg yet */ + draw_tsprite = true; + break; + } + } + } + else if (tsprite_idx >= 0) + { + /* no transparent walls left, draw a sprite */ + draw_tsprite = true; + } + /* fall-through case is draw wall */ + + if (draw_tsprite) + { + /* transparent sprite is farther, draw it */ + glAlphaFunc(GL_GEQUAL, gl_mask_sprite_threshold_f); + gld_SetFog(gld_drawinfo.items[GLDIT_TSPRITE][tsprite_idx].item.sprite->fogdensity); + gld_DrawSprite(gld_drawinfo.items[GLDIT_TSPRITE][tsprite_idx].item.sprite); + tsprite_idx--; + } + else + { + glDepthMask(GL_FALSE); + /* transparent wall is farther, draw it */ + glAlphaFunc(GL_GREATER, 0.0f); + gld_SetFog(gld_drawinfo.items[GLDIT_TWALL][twall_idx].item.wall->fogdensity); + gld_ProcessWall(gld_drawinfo.items[GLDIT_TWALL][twall_idx].item.wall); + glDepthMask(GL_TRUE); + twall_idx--; + } + } + glAlphaFunc(GL_GEQUAL, 0.5f); + glEnable(GL_ALPHA_TEST); + } + + // e6y: detail + if (!gl_arb_multitexture && render_usedetail) + gld_DrawDetail_NoARB(); + + gld_EnableDetail(false); + +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + if (!gl_use_display_lists) + { + if (gl_ext_arb_vertex_buffer_object) + { + // bind with 0, so, switch back to normal pointer operation + GLEXT_glBindBufferARB(GL_ARRAY_BUFFER, 0); + } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + } +#endif + + glsl_SetActiveShader(NULL); +} diff --git a/src/gl_map.c b/src/gl_map.c new file mode 100644 index 0000000..a1ee4d7 --- /dev/null +++ b/src/gl_map.c @@ -0,0 +1,260 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright 2006 - 2008 G Jackson, Jaakko Kerônen + * Copyright 2009 - 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: Cool automap things + * + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "SDL.h" +#ifdef HAVE_LIBSDL2_IMAGE +#include "SDL_image.h" +#endif + +#include "gl_opengl.h" +#include "gl_intern.h" +#include "w_wad.h" +#include "m_misc.h" +#include "am_map.h" +#include "lprintf.h" + +am_icon_t am_icons[am_icon_count + 1] = +{ + {-1, "M_SHADOW"}, + + {-1, "M_ARROW"}, + {-1, "M_NORMAL"}, + {-1, "M_HEALTH"}, + {-1, "M_ARMOUR"}, + {-1, "M_AMMO"}, + {-1, "M_KEY"}, + {-1, "M_POWER"}, + {-1, "M_WEAP"}, + + {-1, "M_ARROW"}, + {-1, "M_ARROW"}, + {-1, "M_ARROW"}, + {-1, "M_MARK"}, + {-1, "M_NORMAL"}, + + {-1, NULL}, +}; + +typedef struct map_nice_thing_s +{ + vbo_xy_uv_rgba_t v[4]; +} PACKEDATTR map_nice_thing_t; + +static array_t map_things[am_icon_count]; + +void gld_InitMapPics(void) +{ + int i, lump; + + i = 0; + while (am_icons[i].name) + { + lump = (W_CheckNumForName)(am_icons[i].name, ns_prboom); + am_icons[i].lumpnum = lump; + if (lump != -1) + { + SDL_Surface *surf = NULL; +#ifdef HAVE_LIBSDL2_IMAGE + SDL_Surface *surf_raw; + + surf_raw = IMG_Load_RW(SDL_RWFromConstMem(W_CacheLumpNum(lump), W_LumpLength(lump)), true); + + surf = SDL_ConvertSurface(surf_raw, &RGBAFormat, 0); + SDL_FreeSurface(surf_raw); +#endif + + W_UnlockLumpNum(lump); + + if (surf) + { + glGenTextures(1, &am_icons[i].tex_id); + glBindTexture(GL_TEXTURE_2D, am_icons[i].tex_id); + + if (gl_arb_texture_non_power_of_two) + { + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); + glTexImage2D(GL_TEXTURE_2D, 0, gl_tex_format, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); + } + else + { + gluBuild2DMipmaps(GL_TEXTURE_2D, gl_tex_format, surf->w, surf->h, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);//tex_filter[MIP_PATCH].min_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//tex_filter[MIP_PATCH].mag_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + SDL_FreeSurface(surf); + } + } + + i++; + } +} + +void gld_AddNiceThing(int type, float x, float y, float radius, float angle, + unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + map_nice_thing_t *thing = M_ArrayGetNewItem(&map_things[type], sizeof(thing[0])); + + float sina_r = (float)sin(angle) * radius; + float cosa_r = (float)cos(angle) * radius; + +#define MAP_NICE_THING_INIT(index, _x, _y, _u, _v) \ + { \ + thing->v[index].x = _x; \ + thing->v[index].y = _y; \ + thing->v[index].u = _u; \ + thing->v[index].v = _v; \ + thing->v[index].r = r; \ + thing->v[index].g = g; \ + thing->v[index].b = b; \ + thing->v[index].a = a; \ + } \ + + MAP_NICE_THING_INIT(0, x + sina_r + cosa_r, y - cosa_r + sina_r, 1.0f, 0.0f); + MAP_NICE_THING_INIT(1, x + sina_r - cosa_r, y - cosa_r - sina_r, 0.0f, 0.0f); + MAP_NICE_THING_INIT(2, x - sina_r - cosa_r, y + cosa_r - sina_r, 0.0f, 1.0f); + MAP_NICE_THING_INIT(3, x - sina_r + cosa_r, y + cosa_r + sina_r, 1.0f, 1.0f); + +#undef MAP_NICE_THING_INIT +} + +void gld_DrawNiceThings(int fx, int fy, int fw, int fh) +{ + int i; + int j; + + glScissor(fx, SCREENHEIGHT - (fy + fh), fw, fh); + glEnable(GL_SCISSOR_TEST); + + glDisable(GL_ALPHA_TEST); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gld_EnableTexture2D(GL_TEXTURE0_ARB, true); + +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + // activate vertex array, texture coord array and color arrays + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); +#endif + + for (i = 0; i < am_icon_count; i++) + { + array_t *things = &map_things[i]; + + if (things->count == 0) + continue; + + glBindTexture(GL_TEXTURE_2D, am_icons[i].tex_id); + +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + { + map_nice_thing_t *thing = &((map_nice_thing_t*)things->data)[0]; + + // activate and specify pointers to arrays + glVertexPointer(2, GL_FLOAT, sizeof(thing->v[0]), &thing->v[0].x); + glTexCoordPointer(2, GL_FLOAT, sizeof(thing->v[0]), &thing->v[0].u); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(thing->v[0]), &thing->v[0].r); + + glDrawArrays(GL_QUADS, 0, things->count * 4); + } +#else + for (j = 0; j < things->count; j++) + { + map_nice_thing_t *thing = &((map_nice_thing_t*)things->data)[j]; + + glColor4ubv(&thing->v[0].r); + + glBegin(GL_TRIANGLE_FAN); + { + glTexCoord2f(thing->v[0].u, thing->v[0].v); + glVertex2f(thing->v[0].x, thing->v[0].y); + glTexCoord2f(thing->v[1].u, thing->v[1].v); + glVertex2f(thing->v[1].x, thing->v[1].y); + glTexCoord2f(thing->v[2].u, thing->v[2].v); + glVertex2f(thing->v[2].x, thing->v[2].y); + glTexCoord2f(thing->v[3].u, thing->v[3].v); + glVertex2f(thing->v[3].x, thing->v[3].y); + } + glEnd(); + } +#endif + } + +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + // deactivate vertex array, texture coord array and color arrays + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); +#endif + + gld_ResetLastTexture(); + glDisable(GL_SCISSOR_TEST); +} + +void gld_ClearNiceThings(void) +{ + int type; + + for (type = 0; type < am_icon_count; type++) + { + M_ArrayClear(&map_things[type]); + } +} + +void gld_DrawMapLines(void) +{ +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + if (map_lines.count > 0) + { + map_point_t *point = (map_point_t*)map_lines.data; + + gld_EnableTexture2D(GL_TEXTURE0_ARB, false); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + glVertexPointer(2, GL_FLOAT, sizeof(point[0]), &point->x); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(point[0]), &point->r); + + glDrawArrays(GL_LINES, 0, map_lines.count * 2); + + gld_EnableTexture2D(GL_TEXTURE0_ARB, true); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + } +#endif +} diff --git a/src/gl_missingtexture.c b/src/gl_missingtexture.c new file mode 100644 index 0000000..c847bc5 --- /dev/null +++ b/src/gl_missingtexture.c @@ -0,0 +1,541 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gl_opengl.h" + +#include +#ifdef HAVE_LIBSDL2_IMAGE +#include +#endif +#include "doomstat.h" +#include "v_video.h" +#include "gl_intern.h" +#include "i_system.h" +#include "lprintf.h" +#include "i_video.h" +#include "hu_lib.h" +#include "hu_stuff.h" +#include "r_main.h" +#include "e6y.h" + +typedef struct +{ + int count; // size of the list with adjoining sectors + int validcount; // finding of the best sector in the group only once in tic + int ceiling; // this group is for ceilings or flats + sector_t *sector; // sector with the 'best' height for the sectors in list + sector_t **list; // list of adjoining sectors +} fakegroup_t; + +typedef struct +{ + sector_t *source; // The sector to receive a fake bleed-through flat + sector_t *target; // The floor sector whose properties should be copied + int ceiling; // Ceiling or floor +} bleedthrough_t; + +static int numfakeplanes = 0; +static fakegroup_t *fakeplanes = NULL; +static sector_t **sectors2 = NULL; +static bleedthrough_t *bleedsectors = NULL; +static int numbleedsectors = 0; + +static void gld_PrepareSectorSpecialEffects(void); +static void gld_PreprocessFakeSector(int ceiling, sector_t *sector, int groupid); +static void gld_RegisterBleedthroughSector(sector_t* source, sector_t* target, int ceiling); + +static void gld_PrepareSectorSpecialEffects(void) +{ + int i, num; + + /* free memory if allocated by previous maps */ + if (bleedsectors) + { + free(bleedsectors); + numbleedsectors = 0; + bleedsectors = NULL; + } + + for (num = 0; num < numsectors; num++) + { + // the following is for specialeffects. see r_bsp.c in R_Subsector + sectors[num].flags |= (NO_TOPTEXTURES | NO_BOTTOMTEXTURES); + + for (i=0; isidenum[0]; + unsigned short sidenum1 = sectors[num].lines[i]->sidenum[1]; + + side_t *side0 = (sidenum0 == NO_INDEX ? NULL : &sides[sidenum0]); + side_t *side1 = (sidenum1 == NO_INDEX ? NULL : &sides[sidenum1]); + + if (side0 && side1) + { + if (side0->toptexture != NO_TEXTURE) + sectors[num].flags &= ~NO_TOPTEXTURES; + if (side0->bottomtexture != NO_TEXTURE) + sectors[num].flags &= ~NO_BOTTOMTEXTURES; + if (side1->toptexture != NO_TEXTURE) + sectors[num].flags &= ~NO_TOPTEXTURES; + if (side1->bottomtexture != NO_TEXTURE) + sectors[num].flags &= ~NO_BOTTOMTEXTURES; + + /* sides should not have null sectors, but we check anyway */ + if (side0->sector && side1->sector) + { + dboolean front_floor_is_sky = (side0->sector->floorpic == skyflatnum); + dboolean front_ceil_is_sky = (side0->sector->ceilingpic == skyflatnum); + + dboolean back_floor_is_sky = (side1->sector->floorpic == skyflatnum); + dboolean back_ceil_is_sky = (side1->sector->ceilingpic == skyflatnum); + + dboolean needs_back_lower = !(front_floor_is_sky) && (side0->sector->floorheight > side1->sector->floorheight); + dboolean needs_front_lower = !(back_floor_is_sky) && (side0->sector->floorheight < side1->sector->floorheight); + + dboolean needs_front_upper = !(back_ceil_is_sky) && (side0->sector->ceilingheight > side1->sector->ceilingheight); + dboolean needs_back_upper = !(front_ceil_is_sky) && (side0->sector->ceilingheight < side1->sector->ceilingheight); + + /* now mark the sectors that may require HOM bleed-through */ + if (needs_front_upper && side0->toptexture == NO_TEXTURE) { + side1->sector->flags |= MISSING_TOPTEXTURES; + gld_RegisterBleedthroughSector(side1->sector,side0->sector,1); + } + + if (needs_back_upper && side1->toptexture == NO_TEXTURE) { + side0->sector->flags |= MISSING_TOPTEXTURES; + gld_RegisterBleedthroughSector(side0->sector,side1->sector,1); + } + + if (needs_back_lower && side1->bottomtexture == NO_TEXTURE) { + side0->sector->flags |= MISSING_BOTTOMTEXTURES; + gld_RegisterBleedthroughSector(side0->sector,side1->sector,0); + } + + if (needs_front_lower && side0->bottomtexture == NO_TEXTURE) { + side1->sector->flags |= MISSING_BOTTOMTEXTURES; + gld_RegisterBleedthroughSector(side1->sector,side0->sector,0); + } + } + } + else + { + sectors[num].flags &= ~NO_TOPTEXTURES; + sectors[num].flags &= ~NO_BOTTOMTEXTURES; + } + } +#ifdef PRBOOM_DEBUG + if (sectors[num].flags & NO_TOPTEXTURES) + lprintf(LO_INFO,"Sector %i has no toptextures\n",num); + if (sectors[num].flags & NO_BOTTOMTEXTURES) + lprintf(LO_INFO,"Sector %i has no bottomtextures\n",num); +#endif + } +} + +static void gld_RegisterBleedthroughSector(sector_t* source, sector_t* target, int ceiling) +{ + int i; + int source_idx = -1; + assert(source); + assert(target); + + /* check whether the sector is processed already */ + for (i = 0; i < numbleedsectors && source_idx == -1; i++) + if (bleedsectors[i].source == source && bleedsectors[i].ceiling == ceiling) + source_idx = i; + + if (source_idx == -1) + { + /* allocate memory for new sector */ + bleedsectors = (bleedthrough_t*) realloc(bleedsectors, (numbleedsectors + 1) * sizeof(bleedthrough_t)); + if(!bleedsectors) I_Error("gld_RegisterBleedthroughSector: Out of memory"); + memset(&bleedsectors[numbleedsectors], 0, sizeof(bleedthrough_t)); + numbleedsectors++; + + source_idx = numbleedsectors - 1; + } + + bleedsectors[source_idx].source = source; + bleedsectors[source_idx].ceiling = ceiling; + + /* either register the proposed target since it is first, + * or check if the new proposed target is a better option + * and register it instead */ + if ((bleedsectors[source_idx].target == NULL) || + (bleedsectors[source_idx].target && + ( + (ceiling && bleedsectors[source_idx].target->ceilingheight > target->ceilingheight) + || + (bleedsectors[source_idx].target->floorheight < target->floorheight) + ) + ) + ) + { + bleedsectors[source_idx].target = target; + } +} + +sector_t* GetBestBleedSector(sector_t* source, int ceiling) +{ + int i; + for (i = 0; i < numbleedsectors; i++) + if (bleedsectors[i].source == source && bleedsectors[i].ceiling == ceiling) + return bleedsectors[i].target; + return NULL; +} + +// +// Recursive mark of all adjoining sectors with no bottom/top texture +// + +static void gld_PreprocessFakeSector(int ceiling, sector_t *sector, int groupid) +{ + int i; + + if (sector->fakegroup[ceiling] != groupid) + { + sector->fakegroup[ceiling] = groupid; + if (groupid >= numfakeplanes) + { + fakeplanes = realloc(fakeplanes, (numfakeplanes + 1) * sizeof(fakegroup_t)); + memset(&fakeplanes[numfakeplanes], 0, sizeof(fakegroup_t)); + numfakeplanes++; + } + sectors2[fakeplanes[groupid].count++] = sector; + } + + for (i = 0; i < sector->linecount; i++) + { + sector_t *sec = NULL; + line_t *line = sector->lines[i]; + + if (line->frontsector && line->frontsector != sector) + { + sec = line->frontsector; + } + else + { + if (line->backsector && line->backsector != sector) + { + sec = line->backsector; + } + } + + if (sec && sec->fakegroup[ceiling] == -1 && + (sec->flags & (ceiling ? NO_TOPTEXTURES : NO_BOTTOMTEXTURES))) + { + gld_PreprocessFakeSector(ceiling, sec, groupid); + } + } +} + +// +// Split of all sectors into groups +// with adjoining sectors with no bottom/top texture +// + +void gld_PreprocessFakeSectors(void) +{ + int i, j, k, ceiling; + int groupid; + + if (gl_use_stencil) + { + // precalculate NO_TOPTEXTURES and NO_BOTTOMTEXTURES flags + gld_PrepareSectorSpecialEffects(); + return; + } + + // free memory + if (fakeplanes) + { + for (i = 0; i < numfakeplanes; i++) + { + fakeplanes[i].count = 0; + free(fakeplanes[i].list); + fakeplanes[i].list = NULL; + } + numfakeplanes = 0; + free(fakeplanes); + fakeplanes = NULL; + } + if (sectors2) + { + free(sectors2); + } + sectors2 = malloc(numsectors * sizeof(sector_t*)); + + // reset all groups with fake floors and ceils + // 0 - floor; 1 - ceil; + for (i = 0; i < numsectors; i++) + { + sectors[i].fakegroup[0] = -1; + sectors[i].fakegroup[1] = -1; + } + + // precalculate NO_TOPTEXTURES and NO_BOTTOMTEXTURES flags + gld_PrepareSectorSpecialEffects(); + + groupid = 0; + + for (ceiling = 0; ceiling <= 1; ceiling++) + { + unsigned int no_texture_flag = (ceiling ? NO_TOPTEXTURES : NO_BOTTOMTEXTURES); + + do + { + for (i = 0; i < numsectors; i++) + { + if (!(sectors[i].flags & no_texture_flag) + && (sectors[i].fakegroup[ceiling] == -1)) + { + gld_PreprocessFakeSector(ceiling, §ors[i], groupid); + fakeplanes[groupid].ceiling = ceiling; + fakeplanes[groupid].list = malloc(fakeplanes[groupid].count * sizeof(sector_t*)); + for (j = 0, k = 0; k < fakeplanes[groupid].count; k++) + { + if (!(sectors2[k]->flags & no_texture_flag)) + { + fakeplanes[groupid].list[j++] = sectors2[k]; + } + } + fakeplanes[groupid].count = j; + groupid++; + break; + } + } + } + while (i < numsectors); + } +} + +// +// Get highest surounding floorheight for flors and +// lowest surounding ceilingheight for ceilings +// + +sector_t* GetBestFake(sector_t *sector, int ceiling, int validcount) +{ + int i; + int groupid = sector->fakegroup[ceiling]; + + if (groupid == -1) + return NULL; + + if (fakeplanes[groupid].validcount != validcount) + { + fakeplanes[groupid].validcount = validcount; + fakeplanes[groupid].sector = NULL; + + if (fakeplanes[groupid].ceiling) + { + fixed_t min_height = INT_MAX; + for (i = 0; i < fakeplanes[groupid].count; i++) + { + if (!(fakeplanes[groupid].list[i]->flags & NO_TOPTEXTURES) && + fakeplanes[groupid].list[i]->ceilingheight < min_height) + { + min_height = fakeplanes[groupid].list[i]->ceilingheight; + fakeplanes[groupid].sector = fakeplanes[groupid].list[i]; + } + } + } + else + { + fixed_t max_height = INT_MIN; + for (i = 0; i < fakeplanes[groupid].count; i++) + { + if (!(fakeplanes[groupid].list[i]->flags & NO_BOTTOMTEXTURES) && + fakeplanes[groupid].list[i]->floorheight > max_height) + { + max_height = fakeplanes[groupid].list[i]->floorheight; + fakeplanes[groupid].sector = fakeplanes[groupid].list[i]; + } + } + } + } + + if (fakeplanes[groupid].sector) + { + if (fakeplanes[groupid].ceiling) + { + if (sector->ceilingheight < fakeplanes[groupid].sector->ceilingheight) + { + return sector; + } + } + else + { + if (sector->floorheight > fakeplanes[groupid].sector->floorheight) + { + return sector; + } + } + } + + return fakeplanes[groupid].sector; +} + +//========================================================================== +// +// Flood gaps with the back side's ceiling/floor texture +// This requires a stencil because the projected plane interferes with +// the depth buffer +// +//========================================================================== + +void gld_SetupFloodStencil(GLWall *wall) +{ + int recursion = 0; + + // Create stencil + glStencilFunc(GL_EQUAL, recursion, ~0); // create stencil + glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // increment stencil of valid pixels + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // don't write to the graphics buffer + gld_EnableTexture2D(GL_TEXTURE0_ARB, false); + glColor3f(1, 1, 1); + glEnable(GL_DEPTH_TEST); + glDepthMask(true); + + glBegin(GL_TRIANGLE_FAN); + glVertex3f(wall->glseg->x1, wall->ytop, wall->glseg->z1); + glVertex3f(wall->glseg->x1, wall->ybottom, wall->glseg->z1); + glVertex3f(wall->glseg->x2, wall->ybottom, wall->glseg->z2); + glVertex3f(wall->glseg->x2, wall->ytop, wall->glseg->z2); + glEnd(); + + glStencilFunc(GL_EQUAL, recursion+1, ~0); // draw sky into stencil + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // this stage doesn't modify the stencil + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // don't write to the graphics buffer + gld_EnableTexture2D(GL_TEXTURE0_ARB, true); + glDisable(GL_DEPTH_TEST); + glDepthMask(false); +} + +void gld_ClearFloodStencil(GLWall *wall) +{ + int recursion = 0; + + glStencilOp(GL_KEEP, GL_KEEP, GL_DECR); + gld_EnableTexture2D(GL_TEXTURE0_ARB, false); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // don't write to the graphics buffer + glColor3f(1, 1, 1); + + glBegin(GL_TRIANGLE_FAN); + glVertex3f(wall->glseg->x1, wall->ytop, wall->glseg->z1); + glVertex3f(wall->glseg->x1, wall->ybottom, wall->glseg->z1); + glVertex3f(wall->glseg->x2, wall->ybottom, wall->glseg->z2); + glVertex3f(wall->glseg->x2, wall->ytop, wall->glseg->z2); + glEnd(); + + // restore old stencil op. + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, recursion, ~0); + gld_EnableTexture2D(GL_TEXTURE0_ARB, true); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glEnable(GL_DEPTH_TEST); + glDepthMask(true); +} + +// +// Calculation of the coordinates of the gap +// +void gld_SetupFloodedPlaneCoords(GLWall *wall, gl_strip_coords_t *c) +{ + float prj_fac1, prj_fac2; + float k = 0.5f; + float ytop, ybottom, planez; + + if (wall->flag == GLDWF_TOPFLUD) + { + ytop = wall->ybottom; + ybottom = wall->ytop; + planez = wall->ybottom; + } + else + { + ytop = wall->ytop; + ybottom = wall->ybottom; + planez = wall->ytop; + } + + prj_fac1 = (ytop - zCamera) / (ytop - zCamera); + prj_fac2 = (ytop - zCamera) / (ybottom - zCamera); + + c->v[0][0] = xCamera + prj_fac1 * (wall->glseg->x1 - xCamera); + c->v[0][1] = planez; + c->v[0][2] = yCamera + prj_fac1 * (wall->glseg->z1 - yCamera); + + c->v[1][0] = xCamera + prj_fac2 * (wall->glseg->x1 - xCamera); + c->v[1][1] = planez; + c->v[1][2] = yCamera + prj_fac2 * (wall->glseg->z1 - yCamera); + + c->v[2][0] = xCamera + prj_fac1 * (wall->glseg->x2 - xCamera); + c->v[2][1] = planez; + c->v[2][2] = yCamera + prj_fac1 * (wall->glseg->z2 - yCamera); + + c->v[3][0] = xCamera + prj_fac2 * (wall->glseg->x2 - xCamera); + c->v[3][1] = planez; + c->v[3][2] = yCamera + prj_fac2 * (wall->glseg->z2 - yCamera); + + c->t[0][0] = -c->v[0][0] / k; + c->t[0][1] = -c->v[0][2] / k; + + c->t[1][0] = -c->v[1][0] / k; + c->t[1][1] = -c->v[1][2] / k; + + c->t[2][0] = -c->v[2][0] / k; + c->t[2][1] = -c->v[2][2] / k; + + c->t[3][0] = -c->v[3][0] / k; + c->t[3][1] = -c->v[3][2] / k; +} + +void gld_SetupFloodedPlaneLight(GLWall *wall) +{ + if (wall->seg->backsector) + { + float light; + light = gld_CalcLightLevel(wall->seg->backsector->lightlevel+(extralight<<5)); + gld_StaticLightAlpha(light, wall->alpha); + } + else + { + gld_StaticLightAlpha(wall->light, wall->alpha); + } +} diff --git a/src/gl_opengl.c b/src/gl_opengl.c new file mode 100644 index 0000000..a44e3cd --- /dev/null +++ b/src/gl_opengl.c @@ -0,0 +1,658 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Thanks Roman "Vortex" Marchenko + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include "gl_opengl.h" + +#include "doomtype.h" +#include "lprintf.h" + +#define isExtensionSupported(ext) strstr(extensions, ext) + +int gl_version; + +static dboolean gl_compatibility_mode; + +int GLEXT_CLAMP_TO_EDGE = GL_CLAMP; +int gl_max_texture_size = 0; + +SDL_PixelFormat RGBAFormat; + +// obsolete? +int gl_use_paletted_texture = 0; +int gl_use_shared_texture_palette = 0; +int gl_paletted_texture = 0; +int gl_shared_texture_palette = 0; + +dboolean gl_ext_texture_filter_anisotropic = false; +dboolean gl_arb_texture_non_power_of_two = false; +dboolean gl_arb_multitexture = false; +dboolean gl_arb_texture_compression = false; +dboolean gl_ext_framebuffer_object = false; +dboolean gl_ext_packed_depth_stencil = false; +dboolean gl_ext_blend_color = false; +dboolean gl_use_stencil = false; +dboolean gl_ext_arb_vertex_buffer_object = false; +dboolean gl_arb_pixel_buffer_object = false; +dboolean gl_arb_shader_objects = false; + +// cfg values +int gl_ext_texture_filter_anisotropic_default; +int gl_arb_texture_non_power_of_two_default; +int gl_arb_multitexture_default; +int gl_arb_texture_compression_default; +int gl_ext_framebuffer_object_default; +int gl_ext_packed_depth_stencil_default; +int gl_ext_blend_color_default; +int gl_use_stencil_default; +int gl_ext_arb_vertex_buffer_object_default; +int gl_arb_pixel_buffer_object_default; +int gl_arb_shader_objects_default; + +int active_texture_enabled[32]; +int clieant_active_texture_enabled[32]; + +// obsolete? +PFNGLCOLORTABLEEXTPROC GLEXT_glColorTableEXT = NULL; + +/* EXT_framebuffer_object */ +PFNGLBINDFRAMEBUFFEREXTPROC GLEXT_glBindFramebufferEXT = NULL; +PFNGLGENFRAMEBUFFERSEXTPROC GLEXT_glGenFramebuffersEXT = NULL; +PFNGLGENRENDERBUFFERSEXTPROC GLEXT_glGenRenderbuffersEXT = NULL; +PFNGLBINDRENDERBUFFEREXTPROC GLEXT_glBindRenderbufferEXT = NULL; +PFNGLRENDERBUFFERSTORAGEEXTPROC GLEXT_glRenderbufferStorageEXT = NULL; +PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC GLEXT_glFramebufferRenderbufferEXT = NULL; +PFNGLFRAMEBUFFERTEXTURE2DEXTPROC GLEXT_glFramebufferTexture2DEXT = NULL; +PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC GLEXT_glCheckFramebufferStatusEXT = NULL; +PFNGLDELETEFRAMEBUFFERSEXTPROC GLEXT_glDeleteFramebuffersEXT = NULL; +PFNGLDELETERENDERBUFFERSEXTPROC GLEXT_glDeleteRenderbuffersEXT = NULL; + +/* ARB_multitexture command function pointers */ +PFNGLACTIVETEXTUREARBPROC GLEXT_glActiveTextureARB = NULL; +PFNGLCLIENTACTIVETEXTUREARBPROC GLEXT_glClientActiveTextureARB = NULL; +PFNGLMULTITEXCOORD2FARBPROC GLEXT_glMultiTexCoord2fARB = NULL; +PFNGLMULTITEXCOORD2FVARBPROC GLEXT_glMultiTexCoord2fvARB = NULL; + +/* ARB_texture_compression */ +PFNGLCOMPRESSEDTEXIMAGE2DARBPROC GLEXT_glCompressedTexImage2DARB = NULL; + +PFNGLBLENDCOLOREXTPROC GLEXT_glBlendColorEXT = NULL; + +/* VBO */ +PFNGLGENBUFFERSARBPROC GLEXT_glGenBuffersARB = NULL; +PFNGLDELETEBUFFERSARBPROC GLEXT_glDeleteBuffersARB = NULL; +PFNGLBINDBUFFERARBPROC GLEXT_glBindBufferARB = NULL; +PFNGLBUFFERDATAARBPROC GLEXT_glBufferDataARB = NULL; + +/* PBO */ +PFNGLBUFFERSUBDATAARBPROC GLEXT_glBufferSubDataARB = NULL; +PFNGLGETBUFFERPARAMETERIVARBPROC GLEXT_glGetBufferParameterivARB = NULL; +PFNGLMAPBUFFERARBPROC GLEXT_glMapBufferARB = NULL; +PFNGLUNMAPBUFFERARBPROC GLEXT_glUnmapBufferARB = NULL; + +/* GL_ARB_shader_objects */ +#ifdef USE_SHADERS +PFNGLDELETEOBJECTARBPROC GLEXT_glDeleteObjectARB = NULL; +PFNGLGETHANDLEARBPROC GLEXT_glGetHandleARB = NULL; +PFNGLDETACHOBJECTARBPROC GLEXT_glDetachObjectARB = NULL; +PFNGLCREATESHADEROBJECTARBPROC GLEXT_glCreateShaderObjectARB = NULL; +PFNGLSHADERSOURCEARBPROC GLEXT_glShaderSourceARB = NULL; +PFNGLCOMPILESHADERARBPROC GLEXT_glCompileShaderARB = NULL; +PFNGLCREATEPROGRAMOBJECTARBPROC GLEXT_glCreateProgramObjectARB = NULL; +PFNGLATTACHOBJECTARBPROC GLEXT_glAttachObjectARB = NULL; +PFNGLLINKPROGRAMARBPROC GLEXT_glLinkProgramARB = NULL; +PFNGLUSEPROGRAMOBJECTARBPROC GLEXT_glUseProgramObjectARB = NULL; +PFNGLVALIDATEPROGRAMARBPROC GLEXT_glValidateProgramARB = NULL; + +PFNGLUNIFORM1FARBPROC GLEXT_glUniform1fARB = NULL; +PFNGLUNIFORM2FARBPROC GLEXT_glUniform2fARB = NULL; +PFNGLUNIFORM1IARBPROC GLEXT_glUniform1iARB = NULL; + +PFNGLGETOBJECTPARAMETERFVARBPROC GLEXT_glGetObjectParameterfvARB = NULL; +PFNGLGETOBJECTPARAMETERIVARBPROC GLEXT_glGetObjectParameterivARB = NULL; +PFNGLGETINFOLOGARBPROC GLEXT_glGetInfoLogARB = NULL; +PFNGLGETATTACHEDOBJECTSARBPROC GLEXT_glGetAttachedObjectsARB = NULL; +PFNGLGETUNIFORMLOCATIONARBPROC GLEXT_glGetUniformLocationARB = NULL; +PFNGLGETACTIVEUNIFORMARBPROC GLEXT_glGetActiveUniformARB = NULL; +PFNGLGETUNIFORMFVARBPROC GLEXT_glGetUniformfvARB = NULL; +#endif + +void gld_InitOpenGLVersion(void) +{ + int MajorVersion, MinorVersion; + gl_version = OPENGL_VERSION_1_0; + if (sscanf((const char*)glGetString(GL_VERSION), "%d.%d", &MajorVersion, &MinorVersion) == 2) + { + if (MajorVersion > 1) + { + gl_version = OPENGL_VERSION_2_0; + if (MinorVersion > 0) gl_version = OPENGL_VERSION_2_1; + } + else + { + gl_version = OPENGL_VERSION_1_0; + if (MinorVersion > 0) gl_version = OPENGL_VERSION_1_1; + if (MinorVersion > 1) gl_version = OPENGL_VERSION_1_2; + if (MinorVersion > 2) gl_version = OPENGL_VERSION_1_3; + if (MinorVersion > 3) gl_version = OPENGL_VERSION_1_4; + if (MinorVersion > 4) gl_version = OPENGL_VERSION_1_5; + } + } +} + +void gld_InitOpenGL(dboolean compatibility_mode) +{ + GLenum texture; + const char *extensions = (const char*)glGetString(GL_EXTENSIONS); + + gl_compatibility_mode = compatibility_mode; + + gld_InitOpenGLVersion(); + + gl_ext_texture_filter_anisotropic = gl_ext_texture_filter_anisotropic_default && + isExtensionSupported("GL_EXT_texture_filter_anisotropic") != NULL; + if (gl_ext_texture_filter_anisotropic) + lprintf(LO_INFO, "using GL_EXT_texture_filter_anisotropic\n"); + + // Any textures sizes are allowed + gl_arb_texture_non_power_of_two = gl_arb_texture_non_power_of_two_default && + isExtensionSupported("GL_ARB_texture_non_power_of_two") != NULL; + if (gl_arb_texture_non_power_of_two) + lprintf(LO_INFO, "using GL_ARB_texture_non_power_of_two\n"); + + // Paletted textures + if (isExtensionSupported("GL_EXT_paletted_texture") != NULL) + { + if (gl_use_paletted_texture) + { + gl_paletted_texture = true; + GLEXT_glColorTableEXT = SDL_GL_GetProcAddress("glColorTableEXT"); + if (GLEXT_glColorTableEXT == NULL) + gl_paletted_texture = false; + else + lprintf(LO_INFO,"using GL_EXT_paletted_texture\n"); + } + } + else if (isExtensionSupported("GL_EXT_shared_texture_palette") != NULL) + { + if (gl_use_shared_texture_palette) + { + gl_shared_texture_palette = true; + GLEXT_glColorTableEXT = SDL_GL_GetProcAddress("glColorTableEXT"); + if (GLEXT_glColorTableEXT == NULL) + gl_shared_texture_palette = false; + else + lprintf(LO_INFO,"using GL_EXT_shared_texture_palette\n"); + } + } + + // + // ARB_multitexture command function pointers + // + + gl_arb_multitexture = gl_arb_multitexture_default && + isExtensionSupported("GL_ARB_multitexture") != NULL; + if (gl_arb_multitexture) + { + GLEXT_glActiveTextureARB = SDL_GL_GetProcAddress("glActiveTextureARB"); + GLEXT_glClientActiveTextureARB = SDL_GL_GetProcAddress("glClientActiveTextureARB"); + GLEXT_glMultiTexCoord2fARB = SDL_GL_GetProcAddress("glMultiTexCoord2fARB"); + GLEXT_glMultiTexCoord2fvARB = SDL_GL_GetProcAddress("glMultiTexCoord2fvARB"); + + if (!GLEXT_glActiveTextureARB || !GLEXT_glClientActiveTextureARB || + !GLEXT_glMultiTexCoord2fARB || !GLEXT_glMultiTexCoord2fvARB) + gl_arb_multitexture = false; + } + if (gl_arb_multitexture) + lprintf(LO_INFO,"using GL_ARB_multitexture\n"); + + // + // ARB_texture_compression + // + + gl_arb_texture_compression = gl_arb_texture_compression_default && + isExtensionSupported("GL_ARB_texture_compression") != NULL; + if (gl_arb_texture_compression) + { + GLEXT_glCompressedTexImage2DARB = SDL_GL_GetProcAddress("glCompressedTexImage2DARB"); + + if (!GLEXT_glCompressedTexImage2DARB) + gl_arb_texture_compression = false; + } + if (gl_arb_texture_compression) + lprintf(LO_INFO,"using GL_ARB_texture_compression\n"); + + // + // EXT_framebuffer_object + // + gl_ext_framebuffer_object = gl_ext_framebuffer_object_default && + isExtensionSupported("GL_EXT_framebuffer_object") != NULL; + if (gl_ext_framebuffer_object) + { + GLEXT_glGenFramebuffersEXT = SDL_GL_GetProcAddress("glGenFramebuffersEXT"); + GLEXT_glBindFramebufferEXT = SDL_GL_GetProcAddress("glBindFramebufferEXT"); + GLEXT_glGenRenderbuffersEXT = SDL_GL_GetProcAddress("glGenRenderbuffersEXT"); + GLEXT_glBindRenderbufferEXT = SDL_GL_GetProcAddress("glBindRenderbufferEXT"); + GLEXT_glRenderbufferStorageEXT = SDL_GL_GetProcAddress("glRenderbufferStorageEXT"); + GLEXT_glFramebufferRenderbufferEXT = SDL_GL_GetProcAddress("glFramebufferRenderbufferEXT"); + GLEXT_glFramebufferTexture2DEXT = SDL_GL_GetProcAddress("glFramebufferTexture2DEXT"); + GLEXT_glCheckFramebufferStatusEXT = SDL_GL_GetProcAddress("glCheckFramebufferStatusEXT"); + GLEXT_glDeleteFramebuffersEXT = SDL_GL_GetProcAddress("glDeleteFramebuffersEXT"); + GLEXT_glDeleteRenderbuffersEXT = SDL_GL_GetProcAddress("glDeleteRenderbuffersEXT"); + + if (!GLEXT_glGenFramebuffersEXT || !GLEXT_glBindFramebufferEXT || + !GLEXT_glGenRenderbuffersEXT || !GLEXT_glBindRenderbufferEXT || + !GLEXT_glRenderbufferStorageEXT || !GLEXT_glFramebufferRenderbufferEXT || + !GLEXT_glFramebufferTexture2DEXT || !GLEXT_glCheckFramebufferStatusEXT || + !GLEXT_glDeleteFramebuffersEXT || !GLEXT_glDeleteRenderbuffersEXT) + gl_ext_framebuffer_object = false; + } + if (gl_ext_framebuffer_object) + lprintf(LO_INFO,"using GL_EXT_framebuffer_object\n"); + + gl_ext_packed_depth_stencil = gl_ext_packed_depth_stencil_default && + isExtensionSupported("GL_EXT_packed_depth_stencil") != NULL; + if (gl_ext_packed_depth_stencil) + lprintf(LO_INFO,"using GL_EXT_packed_depth_stencil\n"); + + // + // Blending + // + + gl_ext_blend_color = gl_ext_blend_color_default && + isExtensionSupported("GL_EXT_blend_color") != NULL; + if (gl_ext_blend_color) + { + GLEXT_glBlendColorEXT = SDL_GL_GetProcAddress("glBlendColorEXT"); + + if (!GLEXT_glBlendColorEXT) + gl_ext_blend_color = false; + } + if (gl_ext_blend_color) + lprintf(LO_INFO,"using GL_EXT_blend_color\n"); + + // VBO +#ifdef USE_VBO + gl_ext_arb_vertex_buffer_object = gl_ext_arb_vertex_buffer_object_default && + isExtensionSupported("GL_ARB_vertex_buffer_object") != NULL; + if (gl_ext_arb_vertex_buffer_object) + { + GLEXT_glGenBuffersARB = SDL_GL_GetProcAddress("glGenBuffersARB"); + GLEXT_glDeleteBuffersARB = SDL_GL_GetProcAddress("glDeleteBuffersARB"); + GLEXT_glBindBufferARB = SDL_GL_GetProcAddress("glBindBufferARB"); + GLEXT_glBufferDataARB = SDL_GL_GetProcAddress("glBufferDataARB"); + + if (!GLEXT_glGenBuffersARB || !GLEXT_glDeleteBuffersARB || + !GLEXT_glBindBufferARB || !GLEXT_glBufferDataARB) + gl_ext_arb_vertex_buffer_object = false; + } + if (gl_ext_arb_vertex_buffer_object) + lprintf(LO_INFO,"using GL_ARB_vertex_buffer_object\n"); +#else + gl_ext_arb_vertex_buffer_object = false; +#endif + + gl_arb_pixel_buffer_object = gl_arb_pixel_buffer_object_default && + isExtensionSupported("GL_ARB_pixel_buffer_object") != NULL; + if (gl_arb_pixel_buffer_object) + { + GLEXT_glGenBuffersARB = SDL_GL_GetProcAddress("glGenBuffersARB"); + GLEXT_glBindBufferARB = SDL_GL_GetProcAddress("glBindBufferARB"); + GLEXT_glBufferDataARB = SDL_GL_GetProcAddress("glBufferDataARB"); + GLEXT_glBufferSubDataARB = SDL_GL_GetProcAddress("glBufferSubDataARB"); + GLEXT_glDeleteBuffersARB = SDL_GL_GetProcAddress("glDeleteBuffersARB"); + GLEXT_glGetBufferParameterivARB = SDL_GL_GetProcAddress("glGetBufferParameterivARB"); + GLEXT_glMapBufferARB = SDL_GL_GetProcAddress("glMapBufferARB"); + GLEXT_glUnmapBufferARB = SDL_GL_GetProcAddress("glUnmapBufferARB"); + + if (!GLEXT_glGenBuffersARB || !GLEXT_glBindBufferARB || + !GLEXT_glBufferDataARB || !GLEXT_glBufferSubDataARB || + !GLEXT_glDeleteBuffersARB || !GLEXT_glGetBufferParameterivARB || + !GLEXT_glMapBufferARB || !GLEXT_glUnmapBufferARB) + gl_arb_pixel_buffer_object = false; + } + if (gl_arb_pixel_buffer_object) + lprintf(LO_INFO,"using GL_ARB_pixel_buffer_object\n"); + + // + // Stencil support + // + + gl_use_stencil = gl_use_stencil_default; + + // + // GL_ARB_shader_objects + // +#ifdef USE_SHADERS + gl_arb_shader_objects = gl_arb_shader_objects_default && + (gl_version >= OPENGL_VERSION_2_0) && + isExtensionSupported ("GL_ARB_shader_objects") && + isExtensionSupported ("GL_ARB_vertex_shader") && + isExtensionSupported ("GL_ARB_fragment_shader") && + isExtensionSupported ("GL_ARB_shading_language_100"); + if (gl_arb_shader_objects) + { + GLEXT_glDeleteObjectARB = SDL_GL_GetProcAddress("glDeleteObjectARB"); + GLEXT_glGetHandleARB = SDL_GL_GetProcAddress("glGetHandleARB"); + GLEXT_glDetachObjectARB = SDL_GL_GetProcAddress("glDetachObjectARB"); + GLEXT_glCreateShaderObjectARB = SDL_GL_GetProcAddress("glCreateShaderObjectARB"); + GLEXT_glShaderSourceARB = SDL_GL_GetProcAddress("glShaderSourceARB"); + GLEXT_glCompileShaderARB = SDL_GL_GetProcAddress("glCompileShaderARB"); + GLEXT_glCreateProgramObjectARB = SDL_GL_GetProcAddress("glCreateProgramObjectARB"); + GLEXT_glAttachObjectARB = SDL_GL_GetProcAddress("glAttachObjectARB"); + GLEXT_glLinkProgramARB = SDL_GL_GetProcAddress("glLinkProgramARB"); + GLEXT_glUseProgramObjectARB = SDL_GL_GetProcAddress("glUseProgramObjectARB"); + GLEXT_glValidateProgramARB = SDL_GL_GetProcAddress("glValidateProgramARB"); + + GLEXT_glUniform1fARB = SDL_GL_GetProcAddress("glUniform1fARB"); + GLEXT_glUniform2fARB = SDL_GL_GetProcAddress("glUniform2fARB"); + GLEXT_glUniform1iARB = SDL_GL_GetProcAddress("glUniform1iARB"); + + GLEXT_glGetObjectParameterfvARB = SDL_GL_GetProcAddress("glGetObjectParameterfvARB"); + GLEXT_glGetObjectParameterivARB = SDL_GL_GetProcAddress("glGetObjectParameterivARB"); + GLEXT_glGetInfoLogARB = SDL_GL_GetProcAddress("glGetInfoLogARB"); + GLEXT_glGetAttachedObjectsARB = SDL_GL_GetProcAddress("glGetAttachedObjectsARB"); + GLEXT_glGetUniformLocationARB = SDL_GL_GetProcAddress("glGetUniformLocationARB"); + GLEXT_glGetActiveUniformARB = SDL_GL_GetProcAddress("glGetActiveUniformARB"); + GLEXT_glGetUniformfvARB = SDL_GL_GetProcAddress("glGetUniformfvARB"); + + if (!GLEXT_glDeleteObjectARB || !GLEXT_glGetHandleARB || + !GLEXT_glDetachObjectARB || !GLEXT_glCreateShaderObjectARB || + !GLEXT_glShaderSourceARB || !GLEXT_glCompileShaderARB || + !GLEXT_glCreateProgramObjectARB || !GLEXT_glAttachObjectARB || + !GLEXT_glLinkProgramARB || !GLEXT_glUseProgramObjectARB || + !GLEXT_glValidateProgramARB || + !GLEXT_glUniform1fARB || !GLEXT_glUniform2fARB || + !GLEXT_glUniform1iARB || + !GLEXT_glGetObjectParameterfvARB || !GLEXT_glGetObjectParameterivARB || + !GLEXT_glGetInfoLogARB || !GLEXT_glGetAttachedObjectsARB || + !GLEXT_glGetUniformLocationARB || !GLEXT_glGetActiveUniformARB || + !GLEXT_glGetUniformfvARB) + gl_arb_shader_objects = false; + } + if (gl_arb_shader_objects) + { + lprintf(LO_INFO,"using GL_ARB_shader_objects\n"); + lprintf(LO_INFO,"using GL_ARB_vertex_shader\n"); + lprintf(LO_INFO,"using GL_ARB_fragment_shader\n"); + lprintf(LO_INFO,"using GL_ARB_shading_language_100\n"); + } +#else + gl_arb_shader_objects = false; +#endif + + // GL_CLAMP_TO_EDGE + GLEXT_CLAMP_TO_EDGE = (gl_version >= OPENGL_VERSION_1_2 ? GL_CLAMP_TO_EDGE : GL_CLAMP); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl_max_texture_size); + lprintf(LO_INFO,"GL_MAX_TEXTURE_SIZE=%i\n", gl_max_texture_size); + + // Additional checks + if (gl_version < OPENGL_VERSION_1_3) + { + gl_ext_framebuffer_object = false; + gl_ext_blend_color = false; + } + + if ((compatibility_mode) || (gl_version <= OPENGL_VERSION_1_1)) + { + lprintf(LO_INFO, "gld_InitOpenGL: Compatibility mode is used.\n"); + gl_arb_texture_non_power_of_two = false; + gl_arb_multitexture = false; + gl_arb_texture_compression = false; + gl_ext_framebuffer_object = false; + gl_ext_packed_depth_stencil = false; + gl_ext_blend_color = false; + gl_use_stencil = false; + gl_ext_arb_vertex_buffer_object = false; + gl_arb_pixel_buffer_object = false; + gl_arb_shader_objects = false; + GLEXT_CLAMP_TO_EDGE = GL_CLAMP; + gl_version = OPENGL_VERSION_1_1; + } + + //init states manager + gld_EnableMultisample(true); + gld_EnableMultisample(false); + + for (texture = GL_TEXTURE0_ARB; texture <= GL_TEXTURE31_ARB; texture++) + { + gld_EnableTexture2D(texture, true); + gld_EnableTexture2D(texture, false); + + gld_EnableClientCoordArray(texture, true); + gld_EnableClientCoordArray(texture, false); + } + + //init global variables + RGBAFormat.palette = 0; + RGBAFormat.BitsPerPixel = 32; + RGBAFormat.BytesPerPixel = 4; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + RGBAFormat.Rmask = 0xFF000000; RGBAFormat.Rshift = 0; RGBAFormat.Rloss = 0; + RGBAFormat.Gmask = 0x00FF0000; RGBAFormat.Gshift = 8; RGBAFormat.Gloss = 0; + RGBAFormat.Bmask = 0x0000FF00; RGBAFormat.Bshift = 16; RGBAFormat.Bloss = 0; + RGBAFormat.Amask = 0x000000FF; RGBAFormat.Ashift = 24; RGBAFormat.Aloss = 0; +#else + RGBAFormat.Rmask = 0x000000FF; RGBAFormat.Rshift = 24; RGBAFormat.Rloss = 0; + RGBAFormat.Gmask = 0x0000FF00; RGBAFormat.Gshift = 16; RGBAFormat.Gloss = 0; + RGBAFormat.Bmask = 0x00FF0000; RGBAFormat.Bshift = 8; RGBAFormat.Bloss = 0; + RGBAFormat.Amask = 0xFF000000; RGBAFormat.Ashift = 0; RGBAFormat.Aloss = 0; +#endif +} + +void gld_EnableTexture2D(GLenum texture, int enable) +{ + int arb; + + if (!gl_arb_multitexture && texture != GL_TEXTURE0_ARB) + return; + + arb = texture - GL_TEXTURE0_ARB; + +#ifdef RANGECHECK + if (arb < 0 || arb > 31) + I_Error("gld_EnableTexture2D: wronge ARB texture unit %d", arb); +#endif + + if (enable) + { + if (!active_texture_enabled[arb]) + { + if (arb != 0) + { + GLEXT_glActiveTextureARB(texture); + glEnable(GL_TEXTURE_2D); + GLEXT_glActiveTextureARB(GL_TEXTURE0_ARB); + } + else + { + glEnable(GL_TEXTURE_2D); + } + active_texture_enabled[arb] = enable; + } + } + else + { + if (active_texture_enabled[arb]) + { + if (arb != 0) + { + GLEXT_glActiveTextureARB(texture); + glDisable(GL_TEXTURE_2D); + GLEXT_glActiveTextureARB(GL_TEXTURE0_ARB); + } + else + { + glDisable(GL_TEXTURE_2D); + } + active_texture_enabled[arb] = enable; + } + } +} + +void gld_EnableClientCoordArray(GLenum texture, int enable) +{ +#ifdef USE_VERTEX_ARRAYS + int arb; + + if (!gl_arb_multitexture) + return; + + arb = texture - GL_TEXTURE0_ARB; + +#ifdef RANGECHECK + if (arb < 0 || arb > 31) + I_Error("gld_EnableTexture2D: wronge ARB texture unit %d", arb); +#endif + + if (enable) + { + if (!clieant_active_texture_enabled[arb]) + { + GLEXT_glClientActiveTextureARB(texture); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + GLEXT_glClientActiveTextureARB(GL_TEXTURE0_ARB); + + clieant_active_texture_enabled[arb] = enable; + } + } + else + { + if (clieant_active_texture_enabled[arb]) + { + GLEXT_glClientActiveTextureARB(texture); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + GLEXT_glClientActiveTextureARB(GL_TEXTURE0_ARB); + + clieant_active_texture_enabled[arb] = enable; + } + } +#endif +} + +void gld_EnableMultisample(int enable) +{ + static int multisample_is_enabled = 0; + if (enable) + { + if (!multisample_is_enabled) + { + glEnable(GL_MULTISAMPLE_ARB); + + multisample_is_enabled = enable; + } + } + else + { + if (multisample_is_enabled) + { + glDisable(GL_MULTISAMPLE_ARB); + + multisample_is_enabled = enable; + } + } +} + +void SetTextureMode(tex_mode_e type) +{ + if (gl_compatibility_mode) + { + type = TM_MODULATE; + } + + if (type == TM_MASK) + { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + } + else if (type == TM_OPAQUE) + { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + } + else if (type == TM_INVERT) + { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + } + else if (type == TM_INVERTOPAQUE) + { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + } + else // if (type == TM_MODULATE) + { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } +} diff --git a/src/gl_opengl.h b/src/gl_opengl.h new file mode 100644 index 0000000..59d7857 --- /dev/null +++ b/src/gl_opengl.h @@ -0,0 +1,191 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Thanks Roman "Vortex" Marchenko + *--------------------------------------------------------------------- + */ + +#ifndef _GL_OPENGL_H +#define _GL_OPENGL_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define USE_VERTEX_ARRAYS +//#define USE_VBO + +#include +#include + +#if SDL_VERSION_ATLEAST(1, 3, 0) +#if defined(__MACOSX__) +#include /* Header File For The OpenGL Library */ +#include /* Header File For The GLU Library */ +#elif defined(__MACOS__) +#include /* Header File For The OpenGL Library */ +#include /* Header File For The GLU Library */ +#else +#include /* Header File For The OpenGL Library */ +#include /* Header File For The GLU Library */ +#endif +#endif + +#include "doomtype.h" + +#if !defined(GL_DEPTH_STENCIL_EXT) +#define GL_DEPTH_STENCIL_EXT 0x84F9 +#endif + +#define isExtensionSupported(ext) strstr(extensions, ext) + +//e6y: OpenGL version +typedef enum { + OPENGL_VERSION_1_0, + OPENGL_VERSION_1_1, + OPENGL_VERSION_1_2, + OPENGL_VERSION_1_3, + OPENGL_VERSION_1_4, + OPENGL_VERSION_1_5, + OPENGL_VERSION_2_0, + OPENGL_VERSION_2_1, +} glversion_t; + +extern int gl_version; + +extern int GLEXT_CLAMP_TO_EDGE; +extern int gl_max_texture_size; + +extern SDL_PixelFormat RGBAFormat; + +// obsolete? +extern int gl_use_paletted_texture; +extern int gl_use_shared_texture_palette; +extern int gl_paletted_texture; +extern int gl_shared_texture_palette; + +extern dboolean gl_ext_texture_filter_anisotropic; +extern dboolean gl_arb_texture_non_power_of_two; +extern dboolean gl_arb_multitexture; +extern dboolean gl_arb_texture_compression; +extern dboolean gl_ext_framebuffer_object; +extern dboolean gl_ext_packed_depth_stencil; +extern dboolean gl_ext_blend_color; +extern dboolean gl_use_stencil; +extern dboolean gl_ext_arb_vertex_buffer_object; +extern dboolean gl_arb_pixel_buffer_object; +extern dboolean gl_arb_shader_objects; + +// obsolete? +extern PFNGLCOLORTABLEEXTPROC GLEXT_glColorTableEXT; + +extern PFNGLBINDFRAMEBUFFEREXTPROC GLEXT_glBindFramebufferEXT; +extern PFNGLGENFRAMEBUFFERSEXTPROC GLEXT_glGenFramebuffersEXT; +extern PFNGLGENRENDERBUFFERSEXTPROC GLEXT_glGenRenderbuffersEXT; +extern PFNGLBINDRENDERBUFFEREXTPROC GLEXT_glBindRenderbufferEXT; +extern PFNGLRENDERBUFFERSTORAGEEXTPROC GLEXT_glRenderbufferStorageEXT; +extern PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC GLEXT_glFramebufferRenderbufferEXT; +extern PFNGLFRAMEBUFFERTEXTURE2DEXTPROC GLEXT_glFramebufferTexture2DEXT; +extern PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC GLEXT_glCheckFramebufferStatusEXT; +extern PFNGLDELETEFRAMEBUFFERSEXTPROC GLEXT_glDeleteFramebuffersEXT; +extern PFNGLDELETERENDERBUFFERSEXTPROC GLEXT_glDeleteRenderbuffersEXT; + +/* ARB_multitexture command function pointers */ +extern PFNGLACTIVETEXTUREARBPROC GLEXT_glActiveTextureARB; +extern PFNGLCLIENTACTIVETEXTUREARBPROC GLEXT_glClientActiveTextureARB; +extern PFNGLMULTITEXCOORD2FARBPROC GLEXT_glMultiTexCoord2fARB; +extern PFNGLMULTITEXCOORD2FVARBPROC GLEXT_glMultiTexCoord2fvARB; + +extern PFNGLBLENDCOLOREXTPROC GLEXT_glBlendColorEXT; + +/* ARB_texture_compression */ +extern PFNGLCOMPRESSEDTEXIMAGE2DARBPROC GLEXT_glCompressedTexImage2DARB; + +/* VBO */ +extern PFNGLGENBUFFERSARBPROC GLEXT_glGenBuffersARB; +extern PFNGLDELETEBUFFERSARBPROC GLEXT_glDeleteBuffersARB; +extern PFNGLBINDBUFFERARBPROC GLEXT_glBindBufferARB; +extern PFNGLBUFFERDATAARBPROC GLEXT_glBufferDataARB; + +/* PBO */ +extern PFNGLBUFFERSUBDATAARBPROC GLEXT_glBufferSubDataARB; +extern PFNGLGETBUFFERPARAMETERIVARBPROC GLEXT_glGetBufferParameterivARB; +extern PFNGLMAPBUFFERARBPROC GLEXT_glMapBufferARB; +extern PFNGLUNMAPBUFFERARBPROC GLEXT_glUnmapBufferARB; + +/* GL_ARB_shader_objects */ +#ifdef USE_SHADERS +extern PFNGLDELETEOBJECTARBPROC GLEXT_glDeleteObjectARB; +extern PFNGLGETHANDLEARBPROC GLEXT_glGetHandleARB; +extern PFNGLDETACHOBJECTARBPROC GLEXT_glDetachObjectARB; +extern PFNGLCREATESHADEROBJECTARBPROC GLEXT_glCreateShaderObjectARB; +extern PFNGLSHADERSOURCEARBPROC GLEXT_glShaderSourceARB; +extern PFNGLCOMPILESHADERARBPROC GLEXT_glCompileShaderARB; +extern PFNGLCREATEPROGRAMOBJECTARBPROC GLEXT_glCreateProgramObjectARB; +extern PFNGLATTACHOBJECTARBPROC GLEXT_glAttachObjectARB; +extern PFNGLLINKPROGRAMARBPROC GLEXT_glLinkProgramARB; +extern PFNGLUSEPROGRAMOBJECTARBPROC GLEXT_glUseProgramObjectARB; +extern PFNGLVALIDATEPROGRAMARBPROC GLEXT_glValidateProgramARB; + +extern PFNGLUNIFORM1FARBPROC GLEXT_glUniform1fARB; +extern PFNGLUNIFORM2FARBPROC GLEXT_glUniform2fARB; +extern PFNGLUNIFORM1IARBPROC GLEXT_glUniform1iARB; + +extern PFNGLGETOBJECTPARAMETERFVARBPROC GLEXT_glGetObjectParameterfvARB; +extern PFNGLGETOBJECTPARAMETERIVARBPROC GLEXT_glGetObjectParameterivARB; +extern PFNGLGETINFOLOGARBPROC GLEXT_glGetInfoLogARB; +extern PFNGLGETATTACHEDOBJECTSARBPROC GLEXT_glGetAttachedObjectsARB; +extern PFNGLGETUNIFORMLOCATIONARBPROC GLEXT_glGetUniformLocationARB; +extern PFNGLGETACTIVEUNIFORMARBPROC GLEXT_glGetActiveUniformARB; +extern PFNGLGETUNIFORMFVARBPROC GLEXT_glGetUniformfvARB; +#endif + +void gld_InitOpenGL(dboolean compatibility_mode); + +//states +void gld_EnableTexture2D(GLenum texture, int enable); +void gld_EnableClientCoordArray(GLenum texture, int enable); +void gld_EnableMultisample(int enable); + +typedef enum +{ + TMF_MASKBIT = 1, + TMF_OPAQUEBIT = 2, + TMF_INVERTBIT = 4, + + TM_MODULATE = 0, + TM_MASK = TMF_MASKBIT, + TM_OPAQUE = TMF_OPAQUEBIT, + TM_INVERT = TMF_INVERTBIT, + //TM_INVERTMASK = TMF_MASKBIT | TMF_INVERTBIT + TM_INVERTOPAQUE = TMF_INVERTBIT | TMF_OPAQUEBIT, +} tex_mode_e; +void SetTextureMode(tex_mode_e type); + +#endif // _GL_OPENGL_H diff --git a/src/gl_preprocess.c b/src/gl_preprocess.c new file mode 100644 index 0000000..c092d29 --- /dev/null +++ b/src/gl_preprocess.c @@ -0,0 +1,1162 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif +#ifndef CALLBACK +#define CALLBACK +#endif + +#include + +#include "gl_opengl.h" + +#include "z_zone.h" +#include "doomstat.h" +#include "gl_intern.h" +#include "gl_struct.h" +#include "p_maputl.h" +#include "r_main.h" +#include "am_map.h" +#include "lprintf.h" + +#include "m_io.h" + +static FILE *levelinfo; + +static int gld_max_vertexes = 0; +static int gld_num_vertexes = 0; + +static int triangulate_subsectors = 0; + +// this is the list for all sectors to the loops +GLSector *sectorloops; + +// this is the list for all subsectors to the loops +// uses by textured automap +GLMapSubsector *subsectorloops; + +static void gld_AddGlobalVertexes(int count) +{ + if ((gld_num_vertexes+count)>=gld_max_vertexes) + { + gld_max_vertexes+=count+1024; + flats_vbo = Z_Realloc(flats_vbo, gld_max_vertexes * sizeof(flats_vbo[0]), PU_STATIC, 0); + } +} + +/***************************** + * + * FLATS + * + *****************************/ + +/* proff - 05/15/2000 + * The idea and algorithm to compute the flats with nodes and subsectors is + * originaly from JHexen. I have redone it. + */ + +#define FIX2DBL(x) ((double)(x)) +#define MAX_CC_SIDES 128 + +static dboolean gld_PointOnSide(vertex_t *p, divline_t *d) +{ + // We'll return false if the point c is on the left side. + return ((FIX2DBL(d->y)-FIX2DBL(p->y))*FIX2DBL(d->dx)-(FIX2DBL(d->x)-FIX2DBL(p->x))*FIX2DBL(d->dy) >= 0); +} + +// Lines start-end and fdiv must intersect. +static void gld_CalcIntersectionVertex(vertex_t *s, vertex_t *e, divline_t *d, vertex_t *i) +{ + double ax = FIX2DBL(s->x), ay = FIX2DBL(s->y), bx = FIX2DBL(e->x), by = FIX2DBL(e->y); + double cx = FIX2DBL(d->x), cy = FIX2DBL(d->y), dx = cx+FIX2DBL(d->dx), dy = cy+FIX2DBL(d->dy); + double r = ((ay-cy)*(dx-cx)-(ax-cx)*(dy-cy)) / ((bx-ax)*(dy-cy)-(by-ay)*(dx-cx)); + i->x = (fixed_t)((double)s->x + r*((double)e->x-(double)s->x)); + i->y = (fixed_t)((double)s->y + r*((double)e->y-(double)s->y)); +} + +#undef FIX2DBL + +// Returns a pointer to the list of points. It must be used. +// +static vertex_t *gld_FlatEdgeClipper(int *numpoints, vertex_t *points, int numclippers, divline_t *clippers) +{ + unsigned char sidelist[MAX_CC_SIDES]; + int i, k, num = *numpoints; + + // We'll clip the polygon with each of the divlines. The left side of + // each divline is discarded. + for(i=0; i= MAX_CC_SIDES) + I_Error("gld_FlatEdgeClipper: Too many points in carver"); + + // Make room for the new vertex. + memmove(&points[endIdx+1], &points[endIdx], + (num - endIdx-1)*sizeof(vertex_t)); + memcpy(&points[endIdx], &newvert, sizeof(newvert)); + + memmove(&sidelist[endIdx+1], &sidelist[endIdx], num-endIdx-1); + sidelist[endIdx] = 1; + + // Skip over the new vertex. + k++; + } + } + + // Now we must discard the points that are on the wrong side. + for(k=0; knumlines; + divline_t *clippers; + int i, numedgepoints; + vertex_t *edgepoints; + + clippers=(divline_t*)Z_Malloc(numclippers*sizeof(divline_t),PU_LEVEL,0); + if (!clippers) + return; + for(i=0; ifirstline+i-num]; + clippers[i].x = seg->v1->x; + clippers[i].y = seg->v1->y; + clippers[i].dx = seg->v2->x-seg->v1->x; + clippers[i].dy = seg->v2->y-seg->v1->y; + } + + // Setup the 'worldwide' polygon. + numedgepoints = 4; + edgepoints = (vertex_t*)Z_Malloc(numedgepoints*sizeof(vertex_t),PU_LEVEL,0); + + edgepoints[0].x = INT_MIN; + edgepoints[0].y = INT_MAX; + + edgepoints[1].x = INT_MAX; + edgepoints[1].y = INT_MAX; + + edgepoints[2].x = INT_MAX; + edgepoints[2].y = INT_MIN; + + edgepoints[3].x = INT_MIN; + edgepoints[3].y = INT_MIN; + + // Do some clipping, + edgepoints = gld_FlatEdgeClipper(&numedgepoints, edgepoints, numclippers, clippers); + + if(!numedgepoints) + { + if (levelinfo) fprintf(levelinfo, "All carved away: subsector %li - sector %i\n", ssec-subsectors, ssec->sector->iSectorID); + } + else + { + if(numedgepoints >= 3) + { + gld_AddGlobalVertexes(numedgepoints); + if (flats_vbo) + { + int currentsector=ssec->sector->iSectorID; + GLLoopDef **loop; + int *loopcount; + + if (triangulate_subsectors) + { + loop = &subsectorloops[ ssidx ].loops; + loopcount = &subsectorloops[ ssidx ].loopcount; + } + else + { + loop = §orloops[ currentsector ].loops; + loopcount = §orloops[ currentsector ].loopcount; + } + + (*loopcount)++; + (*loop) = Z_Realloc((*loop), sizeof(GLLoopDef)*(*loopcount), PU_STATIC, 0); + ((*loop)[(*loopcount) - 1]).index = ssidx; + ((*loop)[(*loopcount) - 1]).mode = GL_TRIANGLE_FAN; + ((*loop)[(*loopcount) - 1]).vertexcount = numedgepoints; + ((*loop)[(*loopcount) - 1]).vertexindex = gld_num_vertexes; + + for(i = 0; i < numedgepoints; i++) + { + flats_vbo[gld_num_vertexes].u = ( (float)edgepoints[i].x/(float)FRACUNIT)/64.0f; + flats_vbo[gld_num_vertexes].v = (-(float)edgepoints[i].y/(float)FRACUNIT)/64.0f; + flats_vbo[gld_num_vertexes].x = -(float)edgepoints[i].x/MAP_SCALE; + flats_vbo[gld_num_vertexes].y = 0.0f; + flats_vbo[gld_num_vertexes].z = (float)edgepoints[i].y/MAP_SCALE; + gld_num_vertexes++; + } + } + } + } + // We're done, free the edgepoints memory. + Z_Free(edgepoints); + Z_Free(clippers); +} + +static void gld_CarveFlats(int bspnode, int numdivlines, divline_t *divlines) +{ + node_t *nod; + divline_t *childlist, *dl; + int childlistsize = numdivlines+1; + + // If this is a subsector we are dealing with, begin carving with the + // given list. + if(bspnode & NF_SUBSECTOR) + { + // We have arrived at a subsector. The divline list contains all + // the partition lines that carve out the subsector. + // special case for trivial maps (no nodes, single subsector) + int ssidx = (numnodes != 0) ? bspnode & (~NF_SUBSECTOR) : 0; + + if (!(subsectors[ssidx].sector->flags & SECTOR_IS_CLOSED) || triangulate_subsectors) + gld_FlatConvexCarver(ssidx, numdivlines, divlines); + return; + } + + // Get a pointer to the node. + nod = nodes + bspnode; + + // Allocate a new list for each child. + childlist = (divline_t*)Z_Malloc(childlistsize*sizeof(divline_t),PU_LEVEL,0); + + // Copy the previous lines. + if(divlines) memcpy(childlist,divlines,numdivlines*sizeof(divline_t)); + + dl = childlist + numdivlines; + dl->x = nod->x; + dl->y = nod->y; + // The right child gets the original line (LEFT side clipped). + dl->dx = nod->dx; + dl->dy = nod->dy; + gld_CarveFlats(nod->children[0],childlistsize,childlist); + + // The left side. We must reverse the line, otherwise the wrong + // side would get clipped. + dl->dx = -nod->dx; + dl->dy = -nod->dy; + gld_CarveFlats(nod->children[1],childlistsize,childlist); + + // We are finishing with this node, free the allocated list. + Z_Free(childlist); +} + +#ifdef USE_GLU_TESS + +static int currentsector; // the sector which is currently tesselated + +// ntessBegin +// +// called when the tesselation of a new loop starts + +static void CALLBACK ntessBegin( GLenum type ) +{ +#ifdef PRBOOM_DEBUG + if (levelinfo) + { + if (type==GL_TRIANGLES) + fprintf(levelinfo, "\t\tBegin: GL_TRIANGLES\n"); + else + if (type==GL_TRIANGLE_FAN) + fprintf(levelinfo, "\t\tBegin: GL_TRIANGLE_FAN\n"); + else + if (type==GL_TRIANGLE_STRIP) + fprintf(levelinfo, "\t\tBegin: GL_TRIANGLE_STRIP\n"); + else + fprintf(levelinfo, "\t\tBegin: unknown\n"); + } +#endif + // increase loopcount for currentsector + sectorloops[ currentsector ].loopcount++; + // reallocate to get space for another loop + // PU_LEVEL is used, so this gets freed before a new level is loaded + sectorloops[ currentsector ].loops=Z_Realloc(sectorloops[currentsector].loops,sizeof(GLLoopDef)*sectorloops[currentsector].loopcount, PU_STATIC, 0); + // set initial values for current loop + // currentloop is -> sectorloops[currentsector].loopcount-1 + sectorloops[ currentsector ].loops[ sectorloops[currentsector].loopcount-1 ].index=-1; + sectorloops[ currentsector ].loops[ sectorloops[currentsector].loopcount-1 ].mode=type; + sectorloops[ currentsector ].loops[ sectorloops[currentsector].loopcount-1 ].vertexcount=0; + sectorloops[ currentsector ].loops[ sectorloops[currentsector].loopcount-1 ].vertexindex=gld_num_vertexes; +} + +// ntessError +// +// called when the tesselation failes (DEBUG only) + +static void CALLBACK ntessError(GLenum error) +{ +#ifdef PRBOOM_DEBUG + const GLubyte *estring; + estring = gluErrorString(error); + fprintf(levelinfo, "\t\tTessellation Error: %s\n", estring); +#endif +} + +// ntessCombine +// +// called when the two or more vertexes are on the same coordinate + +static void CALLBACK ntessCombine( GLdouble coords[3], vertex_t *vert[4], GLfloat w[4], void **dataOut ) +{ +#ifdef PRBOOM_DEBUG + if (levelinfo) + { + fprintf(levelinfo, "\t\tVertexCombine Coords: x %10.5f, y %10.5f z %10.5f\n", coords[0], coords[1], coords[2]); + if (vert[0]) fprintf(levelinfo, "\t\tVertexCombine Vert1 : x %10i, y %10i p %p\n", vert[0]->x>>FRACBITS, vert[0]->y>>FRACBITS, vert[0]); + if (vert[1]) fprintf(levelinfo, "\t\tVertexCombine Vert2 : x %10i, y %10i p %p\n", vert[1]->x>>FRACBITS, vert[1]->y>>FRACBITS, vert[1]); + if (vert[2]) fprintf(levelinfo, "\t\tVertexCombine Vert3 : x %10i, y %10i p %p\n", vert[2]->x>>FRACBITS, vert[2]->y>>FRACBITS, vert[2]); + if (vert[3]) fprintf(levelinfo, "\t\tVertexCombine Vert4 : x %10i, y %10i p %p\n", vert[3]->x>>FRACBITS, vert[3]->y>>FRACBITS, vert[3]); + } +#endif + // just return the first vertex, because all vertexes are on the same coordinate + *dataOut = vert[0]; +} + +// ntessVertex +// +// called when a vertex is found + +static void CALLBACK ntessVertex( vertex_t *vert ) +{ +#ifdef PRBOOM_DEBUG + if (levelinfo) + fprintf(levelinfo, "\t\tVertex : x %10i, y %10i\n", vert->x>>FRACBITS, vert->y>>FRACBITS); +#endif + // increase vertex count + sectorloops[ currentsector ].loops[ sectorloops[currentsector].loopcount-1 ].vertexcount++; + + // increase vertex count + gld_AddGlobalVertexes(1); + // add the new vertex (vert is the second argument of gluTessVertex) + flats_vbo[gld_num_vertexes].u=( (float)vert->x/(float)FRACUNIT)/64.0f; + flats_vbo[gld_num_vertexes].v=(-(float)vert->y/(float)FRACUNIT)/64.0f; + flats_vbo[gld_num_vertexes].x=-(float)vert->x/MAP_SCALE; + flats_vbo[gld_num_vertexes].y=0.0f; + flats_vbo[gld_num_vertexes].z= (float)vert->y/MAP_SCALE; + gld_num_vertexes++; +} + +// ntessEnd +// +// called when the tesselation of a the current loop ends (DEBUG only) + +static void CALLBACK ntessEnd( void ) +{ +#ifdef PRBOOM_DEBUG + if (levelinfo) + fprintf(levelinfo, "\t\tEnd loopcount %i vertexcount %i\n", sectorloops[currentsector].loopcount, sectorloops[ currentsector ].loops[ sectorloops[currentsector].loopcount-1 ].vertexcount); +#endif +} + +// gld_PrecalculateSector +// +// this calculates the loops for the sector "num" +// +// how does it work? +// first I have to credit Michael 'Kodak' Ryssen for the usage of the +// glu tesselation functions. the rest of this stuff is entirely done by me (proff). +// if there are any similarities, then they are implications of the algorithm. +// +// I'm starting with the first line of the current sector. I take it's ending vertex and +// add it to the tesselator. the current line is marked as used. then I'm searching for +// the next line which connects to the current line. if there is more than one line, I +// choose the one with the smallest angle to the current. if there is no next line, I +// start a new loop and take the first unused line in the sector. after all lines are +// processed, the polygon is tesselated. +// +// e6y +// The bug in algorithm of splitting of a sector into the closed contours was fixed. +// There is no more HOM at the starting area on MAP16 @ Eternal.wad +// I hope nothing was broken + +static void gld_PrecalculateSector(int num) +{ + int i; + dboolean *lineadded=NULL; + int linecount; + int currentline; + int oldline; + int currentloop; + int bestline; + int bestlinecount; + vertex_t *startvertex; + vertex_t *currentvertex = NULL; //e6y: fix use of uninitialized local variable below + angle_t lineangle; + angle_t angle; + angle_t bestangle; + GLUtesselator *tess; + double *v=NULL; + int maxvertexnum; + int vertexnum; + + currentsector=num; + lineadded=Z_Malloc(sectors[num].linecount*sizeof(dboolean),PU_LEVEL,0); + if (!lineadded) + { + if (levelinfo) fclose(levelinfo); + return; + } + // init tesselator + tess=gluNewTess(); + if (!tess) + { + if (levelinfo) fclose(levelinfo); + Z_Free(lineadded); + return; + } + // set callbacks + gluTessCallback(tess, GLU_TESS_BEGIN, (void (*)()) ntessBegin); + gluTessCallback(tess, GLU_TESS_VERTEX, (void (*)()) ntessVertex); + gluTessCallback(tess, GLU_TESS_ERROR, (void (*)()) ntessError); + gluTessCallback(tess, GLU_TESS_COMBINE, (void (*)()) ntessCombine); + gluTessCallback(tess, GLU_TESS_END, (void (*)()) ntessEnd); + + /*gluTessCallback(tess, GLU_TESS_BEGIN, ntessBegin); + gluTessCallback(tess, GLU_TESS_VERTEX, ntessVertex); + gluTessCallback(tess, GLU_TESS_ERROR, ntessError); + gluTessCallback(tess, GLU_TESS_COMBINE, ntessCombine); + gluTessCallback(tess, GLU_TESS_END, ntessEnd);*/ + if (levelinfo) fprintf(levelinfo, "sector %i, %i lines in sector\n", num, sectors[num].linecount); + // remove any line which has both sides in the same sector (i.e. Doom2 Map01 Sector 1) + for (i=0; isidenum[0]!=NO_INDEX) + if (sectors[num].lines[i]->sidenum[1]!=NO_INDEX) + if (sides[sectors[num].lines[i]->sidenum[0]].sector + ==sides[sectors[num].lines[i]->sidenum[1]].sector) + { + lineadded[i]=true; + if (levelinfo) fprintf(levelinfo, "line %4i (iLineID %4i) has both sides in same sector (removed)\n", i, sectors[num].lines[i]->iLineID); + } + } + // e6y + // Remove any line which has a clone with the same vertexes and orientation + // (i.e. MM.WAD Map22 lines 1298 and 2397) + // There is no more HOM on Memento Mori MAP22 sector 299 + for (i = 0; i < sectors[num].linecount - 1; i++) + { + int j; + for (j = i + 1; j < sectors[num].linecount; j++) + { + if (sectors[num].lines[i]->v1 == sectors[num].lines[j]->v1 && + sectors[num].lines[i]->v2 == sectors[num].lines[j]->v2 && + sectors[num].lines[i]->frontsector == sectors[num].lines[j]->frontsector && + sectors[num].lines[i]->backsector == sectors[num].lines[j]->backsector && + lineadded[i] == false && lineadded[j] == false) + { + lineadded[i] = true; + } + } + } + + // initialize variables + linecount=sectors[num].linecount; + oldline=0; + currentline=0; + startvertex=sectors[num].lines[currentline]->v2; + currentloop=0; + vertexnum=0; + maxvertexnum=0; + // start tesselator + if (levelinfo) fprintf(levelinfo, "gluTessBeginPolygon\n"); + gluTessBeginPolygon(tess, NULL); + if (levelinfo) fprintf(levelinfo, "\tgluTessBeginContour\n"); + gluTessBeginContour(tess); + while (linecount) + { + // if there is no connected line, then start new loop + if ((oldline==currentline) || (startvertex==currentvertex)) + { + currentline=-1; + for (i=0; isidenum[0]!=NO_INDEX) ? (sides[sectors[num].lines[currentline]->sidenum[0]].sector==§ors[num]) : false) + startvertex=sectors[num].lines[currentline]->v1; + else + startvertex=sectors[num].lines[currentline]->v2; + if (levelinfo) fprintf(levelinfo, "\tNew Loop %3i\n", currentloop); + if (oldline!=0) + { + if (levelinfo) fprintf(levelinfo, "\tgluTessEndContour\n"); + gluTessEndContour(tess); +// if (levelinfo) fprintf(levelinfo, "\tgluNextContour\n"); +// gluNextContour(tess, GLU_CW); + if (levelinfo) fprintf(levelinfo, "\tgluTessBeginContour\n"); + gluTessBeginContour(tess); + } + break; + } + } + if (currentline==-1) + break; + // add current line + lineadded[currentline]=true; + // check if currentsector is on the front side of the line ... + if ((sectors[num].lines[currentline]->sidenum[0]!=NO_INDEX) ? (sides[sectors[num].lines[currentline]->sidenum[0]].sector==§ors[num]) : false) + { + // v2 is ending vertex + currentvertex=sectors[num].lines[currentline]->v2; + // calculate the angle of this line for use below + lineangle = R_PointToAngle2(sectors[num].lines[currentline]->v1->x,sectors[num].lines[currentline]->v1->y,sectors[num].lines[currentline]->v2->x,sectors[num].lines[currentline]->v2->y); + lineangle=(lineangle>>ANGLETOFINESHIFT)*360/8192; + + //e6y: direction of a line shouldn't be changed + //if (lineangle>=180) + // lineangle=lineangle-360; + + if (levelinfo) fprintf(levelinfo, "\t\tAdded Line %4i to Loop, iLineID %5i, Angle: %4i, flipped false\n", currentline, sectors[num].lines[currentline]->iLineID, lineangle); + } + else // ... or on the back side + { + // v1 is ending vertex + currentvertex=sectors[num].lines[currentline]->v1; + // calculate the angle of this line for use below + lineangle = R_PointToAngle2(sectors[num].lines[currentline]->v2->x,sectors[num].lines[currentline]->v2->y,sectors[num].lines[currentline]->v1->x,sectors[num].lines[currentline]->v1->y); + lineangle=(lineangle>>ANGLETOFINESHIFT)*360/8192; + + //e6y: direction of a line shouldn't be changed + //if (lineangle>=180) + // lineangle=lineangle-360; + + if (levelinfo) fprintf(levelinfo, "\t\tAdded Line %4i to Loop, iLineID %5i, Angle: %4i, flipped true\n", currentline, sectors[num].lines[currentline]->iLineID, lineangle); + } + if (vertexnum>=maxvertexnum) + { + maxvertexnum+=512; + v=Z_Realloc(v,maxvertexnum*3*sizeof(double),PU_LEVEL,0); + } + // calculate coordinates for the glu tesselation functions + v[vertexnum*3+0]=-(double)currentvertex->x/(double)MAP_SCALE; + v[vertexnum*3+1]=0.0; + v[vertexnum*3+2]= (double)currentvertex->y/(double)MAP_SCALE; + // add the vertex to the tesselator, currentvertex is the pointer to the vertexlist of doom + // v[vertexnum] is the GLdouble array of the current vertex + if (levelinfo) fprintf(levelinfo, "\t\tgluTessVertex(%i, %i)\n",currentvertex->x>>FRACBITS,currentvertex->y>>FRACBITS); + gluTessVertex(tess, &v[vertexnum*3], currentvertex); + // increase vertexindex + vertexnum++; + // decrease linecount of current sector + linecount--; + // find the next line + oldline=currentline; // if this isn't changed at the end of the search, a new loop will start + bestline=-1; // set to start values + bestlinecount=0; + // set backsector if there is one + /*if (sectors[num].lines[currentline]->sidenum[1]!=NO_INDEX) + backsector=sides[sectors[num].lines[currentline]->sidenum[1]].sector; + else + backsector=NULL;*/ + // search through all lines of the current sector + for (i=0; iv1==currentvertex) || (sectors[num].lines[i]->v2==currentvertex)) + { + // calculate the angle of this best line candidate + if ((sectors[num].lines[i]->sidenum[0]!=NO_INDEX) ? (sides[sectors[num].lines[i]->sidenum[0]].sector==§ors[num]) : false) + angle = R_PointToAngle2(sectors[num].lines[i]->v1->x,sectors[num].lines[i]->v1->y,sectors[num].lines[i]->v2->x,sectors[num].lines[i]->v2->y); + else + angle = R_PointToAngle2(sectors[num].lines[i]->v2->x,sectors[num].lines[i]->v2->y,sectors[num].lines[i]->v1->x,sectors[num].lines[i]->v1->y); + angle=(angle>>ANGLETOFINESHIFT)*360/8192; + + //e6y: direction of a line shouldn't be changed + //if (angle>=180) + // angle=angle-360; + + // check if line is flipped ... + if ((sectors[num].lines[i]->sidenum[0]!=NO_INDEX) ? (sides[sectors[num].lines[i]->sidenum[0]].sector==§ors[num]) : false) + { + // when the line is not flipped and startvertex is not the currentvertex then skip this line + if (sectors[num].lines[i]->v1!=currentvertex) + continue; + } + else + { + // when the line is flipped and endvertex is not the currentvertex then skip this line + if (sectors[num].lines[i]->v2!=currentvertex) + continue; + } + // set new best line candidate + if (bestline==-1) // if this is the first one ... + { + bestline=i; + bestangle=lineangle-angle; + bestlinecount++; + } + else + // check if the angle between the current line and this best line candidate is smaller then + // the angle of the last candidate + // e6y: for finding an angle between AB and BC vectors we should subtract + // (BC - BA) == (BC - (180 - AB)) == (angle-(180-lineangle)) + if (D_abs((int) angle - (180 - (int) lineangle))1) + if (levelinfo) fprintf(levelinfo, "\t\tBestlinecount: %4i\n", bestlinecount); + } + } + // let the tesselator calculate the loops + if (levelinfo) fprintf(levelinfo, "\tgluTessEndContour\n"); + gluTessEndContour(tess); + if (levelinfo) fprintf(levelinfo, "gluTessEndPolygon\n"); + gluTessEndPolygon(tess); + // clean memory + gluDeleteTess(tess); + Z_Free(v); + Z_Free(lineadded); +} + +#endif /* USE_GLU_TESS */ + +/******************************************** + * Name : gld_GetSubSectorVertices * + * created : 08/13/00 * + * modified : 09/18/00, adapted for PrBoom * + * author : figgi * + * what : prepares subsectorvertices * + * (glnodes only) * + ********************************************/ + +static void gld_GetSubSectorVertices(void) +{ + int i, j; + int numedgepoints; + subsector_t* ssector; + + for(i = 0; i < numsubsectors; i++) + { + ssector = &subsectors[i]; + + if ((ssector->sector->flags & SECTOR_IS_CLOSED) && !triangulate_subsectors) + continue; + + numedgepoints = ssector->numlines; + + gld_AddGlobalVertexes(numedgepoints); + + if (flats_vbo) + { + int currentsector = ssector->sector->iSectorID; + GLLoopDef **loop; + int *loopcount; + + if (triangulate_subsectors) + { + loop = &subsectorloops[ i ].loops; + loopcount = &subsectorloops[ i ].loopcount; + } + else + { + loop = §orloops[ currentsector ].loops; + loopcount = §orloops[ currentsector ].loopcount; + } + + (*loopcount)++; + (*loop) = Z_Realloc((*loop), sizeof(GLLoopDef)*(*loopcount), PU_STATIC, 0); + ((*loop)[(*loopcount) - 1]).index = i; + ((*loop)[(*loopcount) - 1]).mode = GL_TRIANGLE_FAN; + ((*loop)[(*loopcount) - 1]).vertexcount = numedgepoints; + ((*loop)[(*loopcount) - 1]).vertexindex = gld_num_vertexes; + + for(j = 0; j < numedgepoints; j++) + { + flats_vbo[gld_num_vertexes].u =( (float)(segs[ssector->firstline + j].v1->x)/FRACUNIT)/64.0f; + flats_vbo[gld_num_vertexes].v =(-(float)(segs[ssector->firstline + j].v1->y)/FRACUNIT)/64.0f; + flats_vbo[gld_num_vertexes].x = -(float)(segs[ssector->firstline + j].v1->x)/MAP_SCALE; + flats_vbo[gld_num_vertexes].y = 0.0f; + flats_vbo[gld_num_vertexes].z = (float)(segs[ssector->firstline + j].v1->y)/MAP_SCALE; + gld_num_vertexes++; + } + } + } +} + +// gld_PreprocessLevel +// +// this checks all sectors if they are closed and calls gld_PrecalculateSector to +// calculate the loops for every sector +// the idea to check for closed sectors is from DEU. check next commentary +/* + Note from RQ: + This is a very simple idea, but it works! The first test (above) + checks that all Sectors are closed. But if a closed set of LineDefs + is moved out of a Sector and has all its "external" SideDefs pointing + to that Sector instead of the new one, then we need a second test. + That's why I check if the SideDefs facing each other are bound to + the same Sector. + + Other note from RQ: + Nowadays, what makes the power of a good editor is its automatic tests. + So, if you are writing another Doom editor, you will probably want + to do the same kind of tests in your program. Fine, but if you use + these ideas, don't forget to credit DEU... Just a reminder... :-) +*/ +// so I credited DEU + +// +// e6y +// All sectors which are inside 64x64 square of map grid should be marked here. +// GL_CLAMP instead of GL_REPEAT should be used for them for avoiding seams +// + +static void gld_MarkSectorsForClamp(void) +{ + int i; + + for (i = 0; i < numsectors; i++) + { + int loopnum; // current loop number + GLLoopDef *currentloop; // the current loop + GLfloat minu, maxu, minv, maxv; + dboolean fail; + + minu = minv = 65535; + maxu = maxv = -65535; + fail = false; + + for (loopnum = 0; !fail && loopnum < sectorloops[i].loopcount; loopnum++) + { + int vertexnum; + // set the current loop + currentloop = §orloops[i].loops[loopnum]; + if (!currentloop) + continue; + for (vertexnum = currentloop->vertexindex; !fail && vertexnum<(currentloop->vertexindex+currentloop->vertexcount); vertexnum++) + { + vbo_xyz_uv_t *vbo; + vbo = &flats_vbo[vertexnum]; + + if (vbo->u < minu) minu = (float)floor(vbo->u); + if (vbo->v < minv) minv = (float)floor(vbo->v); + if (vbo->u > maxu) maxu = vbo->u; + if (vbo->v > maxv) maxv = vbo->v; + + fail = (maxu - minu > 1.0f || maxv - minv > 1.0f); + } + } + + if (!fail) + { + sectorloops[i].flags = SECTOR_CLAMPXY; + + for (loopnum=0; loopnumvertexindex; vertexnum < (currentloop->vertexindex+currentloop->vertexcount); vertexnum++) + { + flats_vbo[vertexnum].u = flats_vbo[vertexnum].u - minu; + flats_vbo[vertexnum].v = flats_vbo[vertexnum].v - minv; + } + } + } + } +} + +static void gld_PreprocessSectors(void) +{ +#ifdef USE_GLU_TESS // figgi + char *vertexcheck = NULL; + char *vertexcheck2 = NULL; + int v1num; + int v2num; + int i; + int j; +#endif + +#ifdef PRBOOM_DEBUG + levelinfo=M_fopen("levelinfo.txt","a"); + if (levelinfo) + { + if (gamemode==commercial) + fprintf(levelinfo,"MAP%02i\n",gamemap); + else + fprintf(levelinfo,"E%iM%i\n",gameepisode,gamemap); + } +#endif + + if (numsectors) + { + sectorloops=Z_Malloc(sizeof(GLSector)*numsectors,PU_STATIC,0); + if (!sectorloops) + I_Error("gld_PreprocessSectors: Not enough memory for array sectorloops"); + memset(sectorloops, 0, sizeof(GLSector)*numsectors); + } + + if (numsubsectors) + { + subsectorloops=Z_Malloc(sizeof(GLMapSubsector)*numsubsectors,PU_STATIC,0); + if (!subsectorloops) + I_Error("gld_PreprocessSectors: Not enough memory for array subsectorloops"); + memset(subsectorloops, 0, sizeof(GLMapSubsector)*numsubsectors); + } + + if (numsegs) + { + segrendered=calloc(numsegs, sizeof(byte)); + if (!segrendered) + I_Error("gld_PreprocessSectors: Not enough memory for array segrendered"); + } + + if (numlines) + { + linerendered[0]=calloc(numlines, sizeof(byte)); + linerendered[1]=calloc(numlines, sizeof(byte)); + if (!linerendered[0] || !linerendered[1]) + I_Error("gld_PreprocessSectors: Not enough memory for array linerendered"); + } + + flats_vbo = NULL; + gld_max_vertexes=0; + gld_num_vertexes=0; + if (numvertexes) + { + gld_AddGlobalVertexes(numvertexes*2); + } + +#ifdef USE_GLU_TESS + if (numvertexes) + { + vertexcheck=malloc(numvertexes*sizeof(vertexcheck[0])); + vertexcheck2=malloc(numvertexes*sizeof(vertexcheck2[0])); + if (!vertexcheck || !vertexcheck2) + { + if (levelinfo) fclose(levelinfo); + I_Error("gld_PreprocessSectors: Not enough memory for array vertexcheck"); + return; + } + } + + for (i=0; iv1-(intptr_t)vertexes)/sizeof(vertex_t); + v2num=((intptr_t)sectors[i].lines[j]->v2-(intptr_t)vertexes)/sizeof(vertex_t); + if ((v1num>=numvertexes) || (v2num>=numvertexes)) + continue; + + // e6y: for correct handling of missing textures. + // We do not need to apply some algos for isolated lines. + vertexcheck2[v1num]++; + vertexcheck2[v2num]++; + + if (sectors[i].lines[j]->sidenum[0]!=NO_INDEX) + if (sides[sectors[i].lines[j]->sidenum[0]].sector==§ors[i]) + { + vertexcheck[v1num]|=1; + vertexcheck[v2num]|=2; + } + if (sectors[i].lines[j]->sidenum[1]!=NO_INDEX) + if (sides[sectors[i].lines[j]->sidenum[1]].sector==§ors[i]) + { + vertexcheck[v1num]|=2; + vertexcheck[v2num]|=1; + } + } + if (sectors[i].linecount<3) + { +#ifdef PRBOOM_DEBUG + lprintf(LO_ERROR, "sector %i is not closed! %i lines in sector\n", i, sectors[i].linecount); +#endif + if (levelinfo) fprintf(levelinfo, "sector %i is not closed! %i lines in sector\n", i, sectors[i].linecount); + sectors[i].flags &= ~SECTOR_IS_CLOSED; + } + else + { + sectors[i].flags |= SECTOR_IS_CLOSED; + for (j=0; jv1-(intptr_t)vertexes)/sizeof(vertex_t); + v2num=((intptr_t)sectors[i].lines[j]->v2-(intptr_t)vertexes)/sizeof(vertex_t); + if (vertexcheck2[v1num] < 2 && vertexcheck2[v2num] < 2) + { + sectors[i].lines[j]->r_flags |= RF_ISOLATED; + } + } + + // figgi -- adapted for glnodes + if (sectors[i].flags & SECTOR_IS_CLOSED) + gld_PrecalculateSector(i); + } + free(vertexcheck); + free(vertexcheck2); +#endif /* USE_GLU_TESS */ + + // figgi -- adapted for glnodes + if (numnodes) + { + if (nodesVersion == 0) + gld_CarveFlats(numnodes-1, 0, 0); + else + gld_GetSubSectorVertices(); + } + + gld_ProcessTexturedMap(); + + if (levelinfo) fclose(levelinfo); + + //e6y: for seamless rendering + gld_MarkSectorsForClamp(); +} + +static void gld_PreprocessSegs(void) +{ + int i; + + gl_segs=Z_Malloc(numsegs*sizeof(GLSeg),PU_STATIC,0); + for (i=0; ix/(float)MAP_SCALE; + gl_segs[i].z1= (float)segs[i].v1->y/(float)MAP_SCALE; + gl_segs[i].x2=-(float)segs[i].v2->x/(float)MAP_SCALE; + gl_segs[i].z2= (float)segs[i].v2->y/(float)MAP_SCALE; + } + + gl_lines=Z_Malloc(numlines*sizeof(GLSeg),PU_STATIC,0); + for (i=0; ix/(float)MAP_SCALE; + gl_lines[i].z1= (float)lines[i].v1->y/(float)MAP_SCALE; + gl_lines[i].x2=-(float)lines[i].v2->x/(float)MAP_SCALE; + gl_lines[i].z2= (float)lines[i].v2->y/(float)MAP_SCALE; + } +} + +void gld_PreprocessLevel(void) +{ + // e6y: speedup of level reloading + // Do not preprocess GL data twice for same level + if (!gl_preprocessed) + { + int i; + static int numsectors_prev = 0; + static int numsubsectors_prev = 0; + + free(gl_segs); + free(gl_lines); + + free(flats_vbo); + flats_vbo = NULL; + + free(segrendered); + free(linerendered[0]); + free(linerendered[1]); + + for (i = 0; i < numsectors_prev; i++) + { + free(sectorloops[i].loops); + } + free(sectorloops); + for (i = 0; i < numsubsectors_prev; i++) + { + free(subsectorloops[i].loops); + } + free(subsectorloops); + + gld_Precache(); + gld_PreprocessSectors(); + gld_PreprocessFakeSectors(); + gld_PreprocessSegs(); + + numsectors_prev = numsectors; + numsubsectors_prev = numsubsectors; + } + else + { + gld_PreprocessFakeSectors(); + + memset(segrendered, 0, numsegs*sizeof(segrendered[0])); + memset(linerendered[0], 0, numlines*sizeof(linerendered[0][0])); + memset(linerendered[1], 0, numlines*sizeof(linerendered[1][0])); + } + + gld_ResetTexturedAutomap(); + + gld_FreeDrawInfo(); + +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + if (!gl_preprocessed) + { + if (gl_ext_arb_vertex_buffer_object) + { + if (flats_vbo_id) + { + // delete VBO when already exists + GLEXT_glDeleteBuffersARB(1, &flats_vbo_id); + } + // generate a new VBO and get the associated ID + GLEXT_glGenBuffersARB(1, &flats_vbo_id); + // bind VBO in order to use + GLEXT_glBindBufferARB(GL_ARRAY_BUFFER, flats_vbo_id); + // upload data to VBO + GLEXT_glBufferDataARB(GL_ARRAY_BUFFER, + gld_num_vertexes * sizeof(flats_vbo[0]), + flats_vbo, GL_STATIC_DRAW_ARB); + + free(flats_vbo); + flats_vbo = NULL; + + // bind VBO in order to use + GLEXT_glBindBufferARB(GL_ARRAY_BUFFER, flats_vbo_id); + } + + glVertexPointer(3, GL_FLOAT, sizeof(flats_vbo[0]), flats_vbo_x); + glTexCoordPointer(2, GL_FLOAT, sizeof(flats_vbo[0]), flats_vbo_u); + } +#endif + + //e6y + gld_PreprocessDetail(); + gld_InitVertexData(); + +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + if (!gl_preprocessed) + { + if (gl_use_display_lists) + { + gld_InitDisplayLists(); + } + } +#endif + + gl_preprocessed = true; +} + +/***************************** + * + * AUTOMAP + * + *****************************/ + +void gld_ProcessTexturedMap(void) +{ + if (map_textured && subsectorloops && subsectorloops[0].loops == NULL) + { + triangulate_subsectors = 1; + if (nodesVersion == 0) + gld_CarveFlats(numnodes-1, 0, 0); + else + gld_GetSubSectorVertices(); + triangulate_subsectors = 0; + } +} diff --git a/src/gl_shader.c b/src/gl_shader.c new file mode 100644 index 0000000..3e6cff1 --- /dev/null +++ b/src/gl_shader.c @@ -0,0 +1,231 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef USE_SHADERS + +#include +#include +#include +#include "doomstat.h" +#include "v_video.h" +#include "gl_opengl.h" +#include "gl_intern.h" +#include "r_main.h" +#include "w_wad.h" +#include "i_system.h" +#include "r_bsp.h" +#include "lprintf.h" +#include "e6y.h" +#include "r_things.h" + +#include "m_io.h" + +GLShader *sh_main = NULL; +static GLShader *active_shader = NULL; + +static GLShader* gld_LoadShader(const char *vpname, const char *fpname); + +int glsl_Init(void) +{ + static int init = false; + + //if (!init) + { + init = true; + + if (!gl_arb_shader_objects) + { + lprintf(LO_WARN, "glsl_Init: shaders expects OpenGL 2.0\n"); + } + else + { + sh_main = gld_LoadShader("glvp", "glfp"); + } + } + + return (sh_main != NULL); +} + +static int ReadLump(const char *filename, const char *lumpname, unsigned char **buffer) +{ + FILE *file = NULL; + int size = 0; + const unsigned char *data; + int lump; + + file = M_fopen(filename, "r"); + if (file) + { + fseek(file, 0, SEEK_END); + size = ftell(file); + fseek(file, 0, SEEK_SET); + *buffer = malloc(size + 1); + size = fread(*buffer, 1, size, file); + if (size > 0) + { + (*buffer)[size] = 0; + } + fclose(file); + } + else + { + char name[9]; + char* p; + + strncpy(name, lumpname, 9); + name[8] = 0; + for(p = name; *p; p++) + *p = toupper(*p); + + lump = (W_CheckNumForName)(name, ns_prboom); + + if (lump != -1) + { + size = W_LumpLength(lump); + data = W_CacheLumpNum(lump); + *buffer = calloc(1, size + 1); + memcpy (*buffer, data, size); + (*buffer)[size] = 0; + W_UnlockLumpNum(lump); + } + } + + return size; +} + +static GLShader* gld_LoadShader(const char *vpname, const char *fpname) +{ +#define buffer_size 2048 + int idx; + int linked; + char buffer[buffer_size]; + char *vp_data = NULL; + char *fp_data = NULL; + int vp_size, fp_size; + size_t vp_fnlen, fp_fnlen; + char *filename = NULL; + GLShader* shader = NULL; + + vp_fnlen = doom_snprintf(NULL, 0, "%s/shaders/%s.txt", I_DoomExeDir(), vpname); + fp_fnlen = doom_snprintf(NULL, 0, "%s/shaders/%s.txt", I_DoomExeDir(), fpname); + filename = malloc(MAX(vp_fnlen, fp_fnlen) + 1); + + sprintf(filename, "%s/shaders/%s.txt", I_DoomExeDir(), vpname); + vp_size = ReadLump(filename, vpname, &vp_data); + + sprintf(filename, "%s/shaders/%s.txt", I_DoomExeDir(), fpname); + fp_size = ReadLump(filename, fpname, &fp_data); + + if (vp_data && fp_data) + { + shader = calloc(1, sizeof(GLShader)); + + shader->hVertProg = GLEXT_glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); + shader->hFragProg = GLEXT_glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); + + GLEXT_glShaderSourceARB(shader->hVertProg, 1, &vp_data, &vp_size); + GLEXT_glShaderSourceARB(shader->hFragProg, 1, &fp_data, &fp_size); + + GLEXT_glCompileShaderARB(shader->hVertProg); + GLEXT_glCompileShaderARB(shader->hFragProg); + + shader->hShader = GLEXT_glCreateProgramObjectARB(); + + GLEXT_glAttachObjectARB(shader->hShader, shader->hVertProg); + GLEXT_glAttachObjectARB(shader->hShader, shader->hFragProg); + + GLEXT_glLinkProgramARB(shader->hShader); + + GLEXT_glGetInfoLogARB(shader->hShader, buffer_size, NULL, buffer); + + GLEXT_glGetObjectParameterivARB(shader->hShader, GL_OBJECT_LINK_STATUS_ARB, &linked); + + if (linked) + { + lprintf(LO_INFO, "gld_LoadShader: Shader \"%s+%s\" compiled OK: %s\n", vpname, fpname, buffer); + + shader->lightlevel_index = GLEXT_glGetUniformLocationARB(shader->hShader, "lightlevel"); + + GLEXT_glUseProgramObjectARB(shader->hShader); + + idx = GLEXT_glGetUniformLocationARB(shader->hShader, "tex"); + GLEXT_glUniform1iARB(idx, 0); + + GLEXT_glUseProgramObjectARB(0); + } + else + { + lprintf(LO_ERROR, "gld_LoadShader: Error compiling shader \"%s+%s\": %s\n", vpname, fpname, buffer); + free(shader); + shader = NULL; + } + } + + free(filename); + free(vp_data); + free(fp_data); + + return shader; +} + +void glsl_SetActiveShader(GLShader *shader) +{ + if (gl_lightmode == gl_lightmode_shaders) + { + if (shader != active_shader) + { + GLEXT_glUseProgramObjectARB((shader ? shader->hShader : 0)); + active_shader = shader; + } + } +} + +void glsl_SetLightLevel(float lightlevel) +{ + if (sh_main) + { + GLEXT_glUniform1fARB(sh_main->lightlevel_index, lightlevel); + } +} + +int glsl_IsActive(void) +{ + return (gl_lightmode == gl_lightmode_shaders && sh_main); +} + +#endif // USE_SHADERS diff --git a/src/gl_shadow.c b/src/gl_shadow.c new file mode 100644 index 0000000..6978e6c --- /dev/null +++ b/src/gl_shadow.c @@ -0,0 +1,277 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright 2006 - 2008 G Jackson, Jaakko Kernen + * Copyright 2009 - 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: Shadow rendering +* Based on Risen3D implementation which is based on Doomsday v1.7.8. + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gl_opengl.h" +#include "gl_intern.h" +#include "doomstat.h" +#include "p_maputl.h" +#include "w_wad.h" +#include "r_fps.h" +#include "r_bsp.h" +#include "r_sky.h" +#include "r_main.h" +#include "lprintf.h" + +int gl_shadows_maxdist; +int gl_shadows_factor; + +simple_shadow_params_t simple_shadows = +{ + 0, 0, + -1, 0, 0, + 80, 1000, 0.5f, 0.0044f +}; + +//=========================================================================== +// GL_PrepareLightTexture +// The dynamic light map is a 64x64 grayscale 8-bit image. +//=========================================================================== +void gld_InitShadows(void) +{ + int lump; + + simple_shadows.loaded = false; + + simple_shadows.tex_id = -1; + simple_shadows.width = 0; + simple_shadows.height = 0; + + simple_shadows.max_radius = 80; + simple_shadows.max_dist = gl_shadows_maxdist; + simple_shadows.factor = (float)gl_shadows_factor / 256.0f; + simple_shadows.bias = 0.0044f; + + lump = (W_CheckNumForName)("GLSHADOW", ns_prboom); + if (lump != -1) + { + SDL_PixelFormat fmt; + SDL_Surface *surf = NULL; + SDL_Surface *surf_raw; + surf_raw = SDL_LoadBMP_RW(SDL_RWFromConstMem(W_CacheLumpNum(lump), W_LumpLength(lump)), 1); + W_UnlockLumpNum(lump); + + fmt = *surf_raw->format; + fmt.BitsPerPixel = 24; + fmt.BytesPerPixel = 3; + + surf = SDL_ConvertSurface(surf_raw, &fmt, surf_raw->flags); + SDL_FreeSurface(surf_raw); + if (surf) + { + glGenTextures(1, &simple_shadows.tex_id); + glBindTexture(GL_TEXTURE_2D, simple_shadows.tex_id); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, surf->w, surf->h, 0, GL_RGB, GL_UNSIGNED_BYTE, surf->pixels); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + if (gl_ext_texture_filter_anisotropic) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (GLfloat)(1<w; + simple_shadows.height = surf->h; + + SDL_FreeSurface(surf); + } + } + + if (simple_shadows.enable && !simple_shadows.loaded) + { + lprintf(LO_INFO, "gld_InitShadows: failed to initialise shadow texture"); + } +} + +static void gld_DrawShadow(GLShadow *shadow) +{ + glColor3f(shadow->light, shadow->light, shadow->light); + + glBegin(GL_TRIANGLE_FAN); + + glTexCoord2f(1.0f, 0.0f); + glVertex3f(shadow->x + shadow->radius, shadow->z, shadow->y - shadow->radius); + glTexCoord2f(0.0f, 0.0f); + glVertex3f(shadow->x - shadow->radius, shadow->z, shadow->y - shadow->radius); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(shadow->x - shadow->radius, shadow->z, shadow->y + shadow->radius); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(shadow->x + shadow->radius, shadow->z, shadow->y + shadow->radius); + + glEnd(); +} + +//=========================================================================== +// Rend_ProcessThingShadow +// Modified for z-smoothing - R3D +//=========================================================================== +void gld_ProcessThingShadow(mobj_t *mo) +{ + int dist; + float height, moh, halfmoh; + sector_t *sec = mo->subsector->sector; + int radius, z; + fixed_t fz; + GLShadow shadow; + + if (!simple_shadows.enable || !simple_shadows.loaded) + return; + + // Should this mobj have a shadow? + if (mo->flags & (MF_SHADOW|MF_NOBLOCKMAP|MF_NOSECTOR)) + return; + + if (mo->frame & FF_FULLBRIGHT) + return; + + // Don't render mobj shadows on sky floors. + if (mo->subsector->sector->floorpic == skyflatnum) + return; + + if (sectorloops[sec->iSectorID].loopcount <= 0) + return; + + // Is this too far? + dist = P_AproxDistance((mo->x >> 16) - (viewx >> 16), (mo->y >> 16) - (viewy >> 16)); + if (dist > simple_shadows.max_dist) + return; + + // Check the height. + if (sec->heightsec != -1) + z = sectors[sec->heightsec].floorheight; + else + z = sec->floorheight; + + // below visible floor + if (!paused && movement_smooth) + { + fz = mo->PrevZ + FixedMul (tic_vars.frac, mo->z - mo->PrevZ); + } + else + { + fz = mo->z; + } + if (fz < z) + return; + + height = (fz - z) / (float)FRACUNIT; + moh = mo->height / (float)FRACUNIT; + if(!moh) + moh = 1; + + // Too high above floor. + if(height > moh) + return; + + // Calculate the strength of the shadow. + + shadow.light = simple_shadows.factor * sec->lightlevel / 255.0f; + + halfmoh = moh * 0.5f; + if(height > halfmoh) + shadow.light *= 1 - (height - halfmoh) / (moh - halfmoh); + + // Can't be seen. + if(shadow.light <= 0) + return; + + if(shadow.light > 1) + shadow.light = 1; + + // Calculate the radius of the shadow. + radius = mo->info->radius >> 16; + if (radius > mo->patch_width >> 1) + radius = mo->patch_width >> 1; + if(radius > simple_shadows.max_radius) + radius = simple_shadows.max_radius; + if(!radius) + return; + + shadow.radius = radius / MAP_COEFF; + shadow.x = -mo->x / MAP_SCALE; + shadow.y = mo->y / MAP_SCALE; + shadow.z = mo->subsector->sector->floorheight / MAP_SCALE + 0.2f / MAP_COEFF; + + gld_AddDrawItem(GLDIT_SHADOW, &shadow); +} + +//=========================================================================== +// Rend_RenderShadows +//=========================================================================== +void gld_RenderShadows(void) +{ + int i; + + if (!simple_shadows.enable || !simple_shadows.loaded || players[displayplayer].fixedcolormap) + return; + + if (gld_drawinfo.num_items[GLDIT_SHADOW] <= 0) + return; + + if (!gl_ztrick) + { + glDepthRange(simple_shadows.bias, 1); + } + + gl_EnableFog(false); + + glDepthMask(GL_FALSE); + glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + + // Apply a modelview shift. + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + // Scale towards the viewpoint to avoid Z-fighting. + glTranslatef(xCamera, zCamera, yCamera); + glScalef(0.99f, 0.99f, 0.99f); + glTranslatef(-xCamera, -zCamera, -yCamera); + + glBindTexture(GL_TEXTURE_2D, simple_shadows.tex_id); + gld_ResetLastTexture(); + + for (i = gld_drawinfo.num_items[GLDIT_SHADOW] - 1; i >= 0; i--) + { + gld_DrawShadow(gld_drawinfo.items[GLDIT_SHADOW][i].item.shadow); + } + + if (!gl_ztrick) + { + glDepthRange(0, 1); + } + + glPopMatrix(); + glDepthMask(GL_TRUE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} diff --git a/src/gl_sky.c b/src/gl_sky.c new file mode 100644 index 0000000..feac1c3 --- /dev/null +++ b/src/gl_sky.c @@ -0,0 +1,1315 @@ +/* +** gl_sky.cpp +** +** Draws the sky. Loosely based on the JDoom sky and the ZDoomGL 0.66.2 sky. +** +**--------------------------------------------------------------------------- +** Copyright 2003 Tim Stump +** Copyright 2005 Christoph Oelckers +** Copyright 2009 Andrey Budko +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. Full disclosure of the entire project's source code, except for third +** party libraries is mandatory. (NOTE: This clause is non-negotiable!) +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gl_opengl.h" + +#include + +#include "doomstat.h" +#include "v_video.h" +#include "gl_intern.h" +#include "r_plane.h" +#include "r_sky.h" +#include "r_main.h" +#include "sc_man.h" +#include "m_misc.h" +#include "lprintf.h" + +#include "e6y.h" + +typedef struct +{ + int mode; + int vertexcount; + int vertexindex; + int use_texture; +} GLSkyLoopDef; + +typedef struct +{ + int id; + int rows, columns; + int loopcount; + GLSkyLoopDef *loops; + vbo_vertex_t *data; +} GLSkyVBO; + +int gl_skymode; +int gl_drawskys; +// Sky stretching is rather pointless with the GL renderer +// now that it can handle all sky heights. +int gl_stretchsky = false; + +static PalEntry_t *SkyColor; + +SkyBoxParams_t SkyBox; +float y_offset_saved; + +// skybox +box_skybox_t *BoxSkybox = NULL; +int BoxSkyboxCount = 0; + +box_skybox_t *BoxSkybox_default; + +void gld_InitSky(void) +{ + memset(&SkyBox, 0, sizeof(SkyBox)); + SkyBox.index = -1; + y_offset_saved = 0; +} + +void gld_InitFrameSky(void) +{ + SkyBox.type = SKY_NONE; + SkyBox.wall.gltexture = NULL; + SkyBox.x_scale = 0; + SkyBox.y_scale = 0; + SkyBox.x_offset = 0; + SkyBox.y_offset = 0; + SkyBox.side = NULL; +} + +void gld_DrawFakeSkyStrips(void) +{ + int i; + + // This draws a valid z-buffer into the stencil's contents to ensure it + // doesn't get overwritten by the level's geometry. + + // Because some of outdated hardware has no support for + // glColorMask(0, 0, 0, 0) or something, + // I need to render fake strips of sky before dome with using + // full clearing of color buffer (only in compatibility mode) + + if (!gl_compatibility) + { + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no graphics + } + gld_EnableTexture2D(GL_TEXTURE0_ARB, false); + + for (i = gld_drawinfo.num_items[GLDIT_SWALL] - 1; i >= 0; i--) + { + GLWall* wall = gld_drawinfo.items[GLDIT_SWALL][i].item.wall; + + glBegin(GL_TRIANGLE_STRIP); + glVertex3f(wall->glseg->x1,wall->ytop,wall->glseg->z1); + glVertex3f(wall->glseg->x1,wall->ybottom,wall->glseg->z1); + glVertex3f(wall->glseg->x2,wall->ytop,wall->glseg->z2); + glVertex3f(wall->glseg->x2,wall->ybottom,wall->glseg->z2); + glEnd(); + } + + gld_EnableTexture2D(GL_TEXTURE0_ARB, true); + if (!gl_compatibility) + { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + else + { + glClear(GL_COLOR_BUFFER_BIT); + } +} + +void gld_GetScreenSkyScale(GLWall *wall, float *scale_x, float *scale_y) +{ + float sx, sy; + + sx = (wall->flag == GLDWF_SKYFLIP ? -128.0f : 128.0f); + + if (!mlook_or_fov) + { + sx = sx / (float)wall->gltexture->buffer_width; + sy = 200.0f / (wall->gltexture->buffer_height * 1.25f); + } + else + { + sx = sx * skyscale / (float)wall->gltexture->buffer_width; + sy = 127.0f * skyscale / (wall->gltexture->buffer_height * 1.25f); + } + + *scale_x = sx; + *scale_y = sy; +} + +// Sky textures with a zero index should be forced +// See third episode of requiem.wad +void gld_AddSkyTexture(GLWall *wall, int sky1, int sky2, int skytype) +{ + side_t *s = NULL; + line_t *l = NULL; + wall->gltexture = NULL; + + if ((sky1) & PL_SKYFLAT) + { + l = &lines[sky1 & ~PL_SKYFLAT]; + } + else + { + if ((sky2) & PL_SKYFLAT) + { + l = &lines[sky2 & ~PL_SKYFLAT]; + } + } + + if (l) + { + s = *l->sidenum + sides; + SkyBox.side = s; + wall->gltexture = gld_RegisterTexture(texturetranslation[s->toptexture], false, + texturetranslation[s->toptexture] == skytexture || l->special == 271 || l->special == 272); + if (wall->gltexture) + { + if (!mlook_or_fov) + { + wall->skyyaw = -2.0f*((-(float)((viewangle+s->textureoffset)>>ANGLETOFINESHIFT)*360.0f/FINEANGLES)/90.0f); + wall->skyymid = 200.0f/319.5f*(((float)s->rowoffset/(float)FRACUNIT - 28.0f)/100.0f); + } + else + { + wall->skyyaw = -2.0f*(((270.0f-(float)((viewangle+s->textureoffset)>>ANGLETOFINESHIFT)*360.0f/FINEANGLES)+90.0f)/90.0f/skyscale); + wall->skyymid = skyYShift+(((float)s->rowoffset/(float)FRACUNIT + 28.0f)/wall->gltexture->buffer_height)/skyscale; + } + wall->flag = (l->special == 272 ? GLDWF_SKY : GLDWF_SKYFLIP); + } + } + else + { + wall->gltexture = gld_RegisterTexture(skytexture, false, true); + if (wall->gltexture) + { + wall->skyyaw = skyXShift; + wall->skyymid = skyYShift; + wall->flag = GLDWF_SKY; + } + } + + if (wall->gltexture) + { + SkyBox.type |= skytype; + + wall->gltexture->flags |= GLTEXTURE_SKY; + + gld_AddDrawItem(GLDIT_SWALL, wall); + + if (!SkyBox.wall.gltexture) + { + SkyBox.wall = *wall; + + switch (gl_drawskys) + { + case skytype_standard: + gld_GetScreenSkyScale(wall, &SkyBox.x_scale, &SkyBox.y_scale); + break; + + case skytype_screen: + if (s) + { + SkyBox.x_offset = (float)s->textureoffset; + SkyBox.y_offset = (float)s->rowoffset / (float)FRACUNIT; + } + break; + case skytype_skydome: + if (s) + { + SkyBox.x_offset = (float)s->textureoffset * 180.0f / (float)ANG180; + SkyBox.y_offset = (float)s->rowoffset / (float)FRACUNIT; + } + break; + } + } + } +} + +void gld_DrawStripsSky(void) +{ + int i; + float skyymid_multiplier; + GLTexture *gltexture = NULL; + + if (gl_drawskys == skytype_standard) + { + if (comp[comp_skymap] && gl_shared_texture_palette) + glDisable(GL_SHARED_TEXTURE_PALETTE_EXT); + + if (comp[comp_skymap] && (invul_method & INVUL_BW)) + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); + + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + glEnable(GL_TEXTURE_GEN_Q); + if (comp[comp_skymap] || !(invul_method & INVUL_BW)) + glColor4fv(gl_whitecolor); + + SetTextureMode(TM_OPAQUE); + } + + gld_EnableDetail(false); + glMatrixMode(GL_TEXTURE); + + skyymid_multiplier = 1.0f; + if (tallscreen) + { + skyymid_multiplier = (float)ratio_multiplier / ratio_scale; + } + + for (i = gld_drawinfo.num_items[GLDIT_SWALL] - 1; i >= 0; i--) + { + GLWall *wall = gld_drawinfo.items[GLDIT_SWALL][i].item.wall; + + gltexture = (gl_drawskys == skytype_none ? NULL : wall->gltexture); + gld_BindTexture(gltexture, 0); + + if (!gltexture) + { + glColor4f(1.0f,0.0f,0.0f,1.0f); + } + + if (gltexture) + { + float sx, sy; + + glPushMatrix(); + + gld_GetScreenSkyScale(wall, &sx, &sy); + glScalef(sx, sy * skyymid_multiplier, 1.0f); + glTranslatef(wall->skyyaw, wall->skyymid / skyymid_multiplier, 0.0f); + } + +#if 0 + { + float r = (float)(wall->seg->sidedef - sides) / (float)(numsides - 1); + float g = (float)wall->seg->linedef->iLineID / (float)(numlines - 1); + float b = (float)i / (float)(gld_drawinfo.num_items[GLDIT_SWALL] - 1); + glColor4f(r, g, b, 1.0f); + } +#endif + + glBegin(GL_TRIANGLE_STRIP); + glVertex3f(wall->glseg->x1,wall->ytop,wall->glseg->z1); + glVertex3f(wall->glseg->x1,wall->ybottom,wall->glseg->z1); + glVertex3f(wall->glseg->x2,wall->ytop,wall->glseg->z2); + glVertex3f(wall->glseg->x2,wall->ybottom,wall->glseg->z2); + glEnd(); + + if (gltexture) + { + glPopMatrix(); + } + } + + glMatrixMode(GL_MODELVIEW); + + gld_DrawSkyCaps(); + + if (gl_drawskys == skytype_standard) + { + glDisable(GL_TEXTURE_GEN_Q); + glDisable(GL_TEXTURE_GEN_T); + glDisable(GL_TEXTURE_GEN_S); + + if (comp[comp_skymap] && (invul_method & INVUL_BW)) + glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_COMBINE); + + if (comp[comp_skymap] && gl_shared_texture_palette) + glEnable(GL_SHARED_TEXTURE_PALETTE_EXT); + + SetFrameTextureMode(); + } +} + +void gld_DrawSkyCaps(void) +{ + if (SkyBox.type && SkyBox.wall.gltexture) + { + dboolean mlook = GetMouseLook(); + + if (mlook) + { + gld_BindTexture(SkyBox.wall.gltexture, 0); + + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + + glScalef(SkyBox.x_scale, SkyBox.y_scale, 1.0f); + glTranslatef(SkyBox.wall.skyyaw, SkyBox.wall.skyymid, 0.0f); + + if (SkyBox.type & SKY_CEILING) + { + glBegin(GL_TRIANGLE_STRIP); + glVertex3f(-MAXCOORD,+MAXCOORD,+MAXCOORD); + glVertex3f(+MAXCOORD,+MAXCOORD,+MAXCOORD); + glVertex3f(-MAXCOORD,+MAXCOORD,-MAXCOORD); + glVertex3f(+MAXCOORD,+MAXCOORD,-MAXCOORD); + glEnd(); + } + + if (SkyBox.type & SKY_FLOOR) + { + glBegin(GL_TRIANGLE_STRIP); + glVertex3f(-MAXCOORD,-MAXCOORD,+MAXCOORD); + glVertex3f(+MAXCOORD,-MAXCOORD,+MAXCOORD); + glVertex3f(-MAXCOORD,-MAXCOORD,-MAXCOORD); + glVertex3f(+MAXCOORD,-MAXCOORD,-MAXCOORD); + glEnd(); + } + + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + } + } +} + +//=========================================================================== +// +// averageColor +// input is RGBA8 pixel format. +// The resulting RGB color can be scaled uniformly so that the highest +// component becomes one. +// +//=========================================================================== +#define APART(c) (((c)>>24)&0xff) +#define RPART(c) (((c)>>16)&0xff) +#define GPART(c) (((c)>>8)&0xff) +#define BPART(c) ((c)&0xff) + +void averageColor(PalEntry_t * PalEntry, const unsigned int *data, int size, fixed_t maxout_factor) +{ + int i; + int maxv; + unsigned int r, g, b; + + // First clear them. + r = g = b = 0; + if (size == 0) + { + PalEntry->r = 255; + PalEntry->g = 255; + PalEntry->b = 255; + return; + } + for(i = 0; i < size; i++) + { + r += BPART(data[i]); + g += GPART(data[i]); + b += RPART(data[i]); + } + + r = r / size; + g = g / size; + b = b / size; + + maxv=MAX(MAX(r,g),b); + + if(maxv && maxout_factor) + { + maxout_factor = FixedMul(maxout_factor, 255); + r = r * maxout_factor / maxv; + g = g * maxout_factor / maxv; + b = b * maxout_factor / maxv; + } + + PalEntry->r = r; + PalEntry->g = g; + PalEntry->b = b; + return; +} + +// It is an alternative way of drawing the sky (gl_drawskys == skytype_screen) +// This method make sense only for old hardware which have no support for GL_TEXTURE_GEN_* +// Voodoo as example +void gld_DrawScreenSkybox(void) +{ + if (SkyBox.wall.gltexture) + { + #define WRAPANGLE (ANGLE_MAX/4) + + float fU1, fU2, fV1, fV2; + GLWall *wall = &SkyBox.wall; + angle_t angle; + int i, k; + float w; + + if (!gl_compatibility) + { + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no graphics + } + gld_EnableTexture2D(GL_TEXTURE0_ARB, false); + + for (i = gld_drawinfo.num_items[GLDIT_SWALL] - 1; i >= 0; i--) + { + GLWall* wall = gld_drawinfo.items[GLDIT_SWALL][i].item.wall; + + glBegin(GL_TRIANGLE_STRIP); + glVertex3f(wall->glseg->x1,wall->ytop,wall->glseg->z1); + glVertex3f(wall->glseg->x1,wall->ybottom,wall->glseg->z1); + glVertex3f(wall->glseg->x2,wall->ytop,wall->glseg->z2); + glVertex3f(wall->glseg->x2,wall->ybottom,wall->glseg->z2); + glEnd(); + } + + gld_EnableTexture2D(GL_TEXTURE0_ARB, true); + if (!gl_compatibility) + { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + else + { + glClear(GL_COLOR_BUFFER_BIT); + } + + if (!mlook_or_fov) + { + fV1 = SkyBox.y_offset / 127.0f; + fV2 = fV1 + 320.0f / 200.0f; + } + else + { + float f = viewPitch * 2 + 40 / skyscale; + f = BETWEEN(0, 127, f); + fV1 = (f + SkyBox.y_offset) / 127.0f * skyscale; + fV2 = fV1 + 1.0f; + } + + k = MAX(wall->gltexture->buffer_width, 256) / 256; + angle = ((viewangle - ANG45) / k) % WRAPANGLE; + + if (wall->flag == GLDWF_SKYFLIP) + { + fU1 = -((float)angle + SkyBox.x_offset) / (WRAPANGLE - 1); + fU2 = fU1 + 1.0f / k; + } + else + { + fU2 = ((float)angle + SkyBox.x_offset) / (WRAPANGLE - 1); + fU1 = fU2 + 1.0f / k; + } + + glDisable(GL_DEPTH_TEST); + glDisable(GL_ALPHA_TEST); + + glPushMatrix(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + gld_BindTexture(wall->gltexture, 0); + w = 160.0f * SCREENWIDTH / WIDE_SCREENWIDTH; + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(fU1, fV1); glVertex3f(-w, +100.5f, -screen_skybox_zplane); + glTexCoord2f(fU1, fV2); glVertex3f(-w, -100.5f, -screen_skybox_zplane); + glTexCoord2f(fU2, fV1); glVertex3f(+w, +100.5f, -screen_skybox_zplane); + glTexCoord2f(fU2, fV2); glVertex3f(+w, -100.5f, -screen_skybox_zplane); + glEnd(); + + glPopMatrix(); + + glEnable(GL_ALPHA_TEST); + glEnable(GL_DEPTH_TEST); + } +} + +// The texture offset to be applied to the texture coordinates in SkyVertex(). +static int rows, columns; +static dboolean yflip; +static int texw; +static float yMult, yAdd; +static dboolean foglayer; +static float delta = 0.0f; + +int gl_sky_detail = 16; + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void gld_GetSkyCapColors(void) +{ + int color, width, height; + int frame_fixedcolormap_saved; + unsigned char *buffer = NULL; + const unsigned char *playpal = V_GetPlaypal(); + const lighttable_t *colormap; + const lighttable_t *fixedcolormap_saved; + PalEntry_t *ceiling_rgb = &SkyBox.CeilingSkyColor[0]; + PalEntry_t *floor_rgb = &SkyBox.FloorSkyColor[0]; + + // saving current colormap + fixedcolormap_saved = fixedcolormap; + frame_fixedcolormap_saved = frame_fixedcolormap; + + fixedcolormap = fullcolormap; + frame_fixedcolormap = 0; + + gld_BindTexture(SkyBox.wall.gltexture, 0); + + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); + + buffer = malloc(width * height * 4); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + + averageColor(ceiling_rgb, (unsigned int*)buffer, width * MIN(30, height), 0); + + if (height > 30) + { + averageColor(floor_rgb, + ((unsigned int*)buffer) + (height - 30) * width, width * 30, 0); + } + else + { + *floor_rgb = *ceiling_rgb; + } + + colormap = fullcolormap + INVERSECOLORMAP * 256 * sizeof(lighttable_t); + + color = V_BestColor(playpal, ceiling_rgb->r, ceiling_rgb->g, ceiling_rgb->b); + SkyBox.CeilingSkyColor[1].r = playpal[colormap[color] * 3 + 0]; + SkyBox.CeilingSkyColor[1].g = playpal[colormap[color] * 3 + 1]; + SkyBox.CeilingSkyColor[1].b = playpal[colormap[color] * 3 + 2]; + + color = V_BestColor(playpal, floor_rgb->r, floor_rgb->g, floor_rgb->b); + SkyBox.FloorSkyColor[1].r = playpal[colormap[color] * 3 + 0]; + SkyBox.FloorSkyColor[1].g = playpal[colormap[color] * 3 + 1]; + SkyBox.FloorSkyColor[1].b = playpal[colormap[color] * 3 + 2]; + + // restorin current colormap + fixedcolormap = fixedcolormap_saved; + frame_fixedcolormap = frame_fixedcolormap_saved; + + free(buffer); +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +static void SkyVertex(vbo_vertex_t *vbo, int r, int c) +{ + static fixed_t scale = 10000 << FRACBITS; + static angle_t maxSideAngle = ANG180 / 3; + + angle_t topAngle= (angle_t)(c / (float)columns * ANGLE_MAX); + angle_t sideAngle = maxSideAngle * (rows - r) / rows; + fixed_t height = finesine[sideAngle>>ANGLETOFINESHIFT]; + fixed_t realRadius = FixedMul(scale, finecosine[sideAngle>>ANGLETOFINESHIFT]); + fixed_t x = FixedMul(realRadius, finecosine[topAngle>>ANGLETOFINESHIFT]); + fixed_t y = (!yflip) ? FixedMul(scale, height) : FixedMul(scale, height) * -1; + fixed_t z = FixedMul(realRadius, finesine[topAngle>>ANGLETOFINESHIFT]); + float timesRepeat; + + timesRepeat = (short)(4 * (256.0f / texw)); + if (timesRepeat == 0.0f) + timesRepeat = 1.0f; + + if (!foglayer) + { + vbo->r = 255; + vbo->g = 255; + vbo->b = 255; + vbo->a = (r == 0 ? 0 : 255); + + // And the texture coordinates. + if(!yflip) // Flipped Y is for the lower hemisphere. + { + vbo->u = (-timesRepeat * c / (float)columns) ; + vbo->v = (r / (float)rows) * 1.f * yMult + yAdd; + } + else + { + vbo->u = (-timesRepeat * c / (float)columns) ; + vbo->v = ((rows-r)/(float)rows) * 1.f * yMult + yAdd; + } + + if (SkyBox.wall.flag == GLDWF_SKYFLIP) + vbo->u = -vbo->u; + } + + if (r != 4) + { + y += FRACUNIT * 300; + } + + // And finally the vertex. + vbo->x =-(float)x/(float)MAP_SCALE; // Doom mirrors the sky vertically! + vbo->y = (float)y/(float)MAP_SCALE + delta; + vbo->z = (float)z/(float)MAP_SCALE; +} + +GLSkyVBO sky_vbo[2]; + +static void gld_BuildSky(int row_count, int col_count, SkyBoxParams_t *sky, int cm) +{ + int texh, c, r; + vbo_vertex_t *vertex_p; + int vertex_count = 2 * row_count * (col_count * 2 + 2) + col_count * 2; + int vbo_idx = (cm == INVERSECOLORMAP ? 1 : 0); + GLSkyVBO *vbo = &sky_vbo[vbo_idx]; + + if ((vbo->columns != col_count) || (vbo->rows != row_count)) + { + free(vbo->loops); + free(vbo->data); + memset(vbo, 0, sizeof(vbo[0])); + } + + if (!vbo->data) + { + memset(vbo, 0, sizeof(vbo[0])); + vbo->loops = malloc((row_count * 2 + 2) * sizeof(vbo->loops[0])); + // create vertex array + vbo->data = malloc(vertex_count * sizeof(vbo->data[0])); + } + + vbo->columns = col_count; + vbo->rows = row_count; + + texh = sky->wall.gltexture->buffer_height; + if (texh > 190 && gl_stretchsky) + texh = 190; + texw = sky->wall.gltexture->buffer_width; + + vertex_p = &vbo->data[0]; + vbo->loopcount = 0; + for (yflip = 0; yflip < 2; yflip++) + { + vbo->loops[vbo->loopcount].mode = GL_TRIANGLE_FAN; + vbo->loops[vbo->loopcount].vertexindex = vertex_p - &vbo->data[0]; + vbo->loops[vbo->loopcount].vertexcount = col_count; + vbo->loops[vbo->loopcount].use_texture = false; + vbo->loopcount++; + + yAdd = sky->y_offset / texh; + yMult = (texh <= 180 ? 1.0f : 180.0f / texh); + if (yflip == 0) + { + SkyColor = &sky->CeilingSkyColor[vbo_idx]; + } + else + { + SkyColor = &sky->FloorSkyColor[vbo_idx]; + if (texh <= 180) yMult = 1.0f; else yAdd += 180.0f / texh; + } + + delta = 0.0f; + foglayer = true; + for(c = 0; c < col_count; c++) + { + SkyVertex(vertex_p, 1, c); + vertex_p->r = SkyColor->r; + vertex_p->g = SkyColor->g; + vertex_p->b = SkyColor->b; + vertex_p->a = 255; + vertex_p++; + } + foglayer = false; + + delta = (yflip ? 5.0f : -5.0f) / MAP_COEFF; + + for(r = 0; r < row_count; r++) + { + vbo->loops[vbo->loopcount].mode = GL_TRIANGLE_STRIP; + vbo->loops[vbo->loopcount].vertexindex = vertex_p - &vbo->data[0]; + vbo->loops[vbo->loopcount].vertexcount = 2 * col_count + 2; + vbo->loops[vbo->loopcount].use_texture = true; + vbo->loopcount++; + + for(c = 0; c <= col_count; c++) + { + SkyVertex(vertex_p++, r + (yflip ? 1 : 0), (c ? c : 0)); + SkyVertex(vertex_p++, r + (yflip ? 0 : 1), (c ? c : 0)); + } + } + } +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +static void RenderDome(SkyBoxParams_t *sky) +{ + int i, j; + int vbosize; + GLSkyVBO *vbo; + + if (!sky || !sky->wall.gltexture) + return; + + if (invul_method == INVUL_CM && frame_fixedcolormap == INVERSECOLORMAP) + vbo = &sky_vbo[1]; + else + vbo = &sky_vbo[0]; + + +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + // be sure the second ARB is not enabled + gld_EnableDetail(false); +#endif + + glRotatef(-180.0f + sky->x_offset, 0.f, 1.f, 0.f); + + rows = 4; + columns = 4 * gl_sky_detail; + + vbosize = 2 * rows * (columns * 2 + 2) + columns * 2; + + if (sky->y_offset != y_offset_saved || + sky->wall.gltexture->index != sky->index) + { + y_offset_saved = sky->y_offset; + + if (sky->wall.gltexture->index != sky->index) + { + sky->index = sky->wall.gltexture->index; + gld_GetSkyCapColors(); + } + + gld_BuildSky(rows, columns, sky, 0); + gld_BuildSky(rows, columns, sky, INVERSECOLORMAP); + +#ifdef USE_VBO + if (gl_ext_arb_vertex_buffer_object) + { + if (vbo->id) + { + // delete VBO when already exists + GLEXT_glDeleteBuffersARB(1, &vbo->id); + } + // generate a new VBO and get the associated ID + GLEXT_glGenBuffersARB(1, &vbo->id); + // bind VBO in order to use + GLEXT_glBindBufferARB(GL_ARRAY_BUFFER, vbo->id); + // upload data to VBO + GLEXT_glBufferDataARB(GL_ARRAY_BUFFER, + vbosize * sizeof(vbo->data[0]), + vbo->data, GL_STATIC_DRAW_ARB); + } +#endif + } + + gld_BindTexture(SkyBox.wall.gltexture, 0); + +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + if (gl_ext_arb_vertex_buffer_object) + { + // bind VBO in order to use + GLEXT_glBindBufferARB(GL_ARRAY_BUFFER, vbo->id); + } + + // activate and specify pointers to arrays + glVertexPointer(3, GL_FLOAT, sizeof(vbo->data[0]), sky_vbo_x); + glTexCoordPointer(2, GL_FLOAT, sizeof(vbo->data[0]), sky_vbo_u); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vbo->data[0]), sky_vbo_r); + + // activate vertex array, texture coord array and color arrays + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); +#endif + + if (!gl_stretchsky) + { + int texh = sky->wall.gltexture->buffer_height; + + if (texh <= 180) + { + glScalef(1.0f, (float)texh / 230.0f, 1.0f); + } + else + { + if (texh > 190) + glScalef(1.0f, 230.0f / 240.0f, 1.0f); + } + } + + for(j = (HaveMouseLook() || !gl_stretchsky ? 0 : 1); j < 2; j++) + { + gld_EnableTexture2D(GL_TEXTURE0_ARB, j != 0); + + for(i = 0; i < vbo->loopcount; i++) + { + GLSkyLoopDef *loop = &vbo->loops[i]; + + if (j == 0 ? loop->use_texture : !loop->use_texture) + continue; + +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + glDrawArrays(loop->mode, loop->vertexindex, loop->vertexcount); +#else + { + int k; + glBegin(loop->mode); + for (k = loop->vertexindex; k < (loop->vertexindex + loop->vertexcount); k++) + { + vbo_vertex_t *v = &vbo->data[k]; + if (loop->use_texture) + { + glTexCoord2fv((GLfloat*)&v->u); + } + glColor4ubv((GLubyte*)&v->r); + glVertex3fv((GLfloat*)&v->x); + } + glEnd(); + } +#endif + } + } + + glScalef(1.0f, 1.0f, 1.0f); + + // current color is undefined after glDrawArrays + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + +#if defined(USE_VERTEX_ARRAYS) || defined(USE_VBO) + if (gl_ext_arb_vertex_buffer_object) + { + // bind with 0, so, switch back to normal pointer operation + GLEXT_glBindBufferARB(GL_ARRAY_BUFFER, 0); + } + // deactivate color array + glDisableClientState(GL_COLOR_ARRAY); +#endif +} + +void gld_DrawDomeSkyBox(void) +{ + if (SkyBox.wall.gltexture) + { + GLint shading_mode = GL_FLAT; + + gld_DrawFakeSkyStrips(); + + glGetIntegerv(GL_SHADE_MODEL, &shading_mode); + glShadeModel(GL_SMOOTH); + + glDepthMask(false); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_ALPHA_TEST); + SetTextureMode(TM_OPAQUE); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glPushMatrix(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glRotatef(roll, 0.0f, 0.0f, 1.0f); + glRotatef(pitch, 1.0f, 0.0f, 0.0f); + glRotatef(yaw, 0.0f, 1.0f, 0.0f); + glScalef(-2.0f, 2.0f, 2.0f); + glTranslatef(0.f, -1250.0f / MAP_COEFF, 0.f); + + RenderDome(&SkyBox); + + glPopMatrix(); + + glEnable(GL_ALPHA_TEST); + glEnable(GL_DEPTH_TEST); + glDepthMask(true); + + SetFrameTextureMode(); + + glShadeModel(shading_mode); + } +} + +int R_BoxSkyboxNumForName(const char *name) +{ + int i; + + for (i = 0; i < BoxSkyboxCount; i++) + { + if (!strcasecmp(BoxSkybox[i].name, name)) + { + return i; + } + } + + return -1; +} + +void R_SetBoxSkybox(int texture) +{ + int i; + + BoxSkybox_default = NULL; + + for (i = 0; i < BoxSkyboxCount; i++) + { + if (R_CheckTextureNumForName(BoxSkybox[i].name) == texture) + { + BoxSkybox_default = &BoxSkybox[i]; + return; + } + } +} + +box_skybox_t* R_GetBoxSkybox(int index) +{ + if (index >= 0 && index < BoxSkyboxCount) + return &BoxSkybox[index]; + else + return NULL; +} + +void gld_ParseSkybox(void) +{ + if (SC_GetString()) + { + box_skybox_t sb; + memset(&sb, 0, sizeof(sb)); + + strncpy(sb.name, sc_String, 8); + sb.name[8] = 0; + M_Strupr(sb.name); + + while (SC_Check()) + { + SC_GetString(); + if (SC_Compare("fliptop")) + { + sb.fliptop = true; + } + } + + if (SC_GetString() && SC_Compare("{")) + { + int facecount = 0; + + while (SC_GetString() && !SC_Compare("}")) + { + if (facecount < 6) + { + strcpy(sb.faces[facecount], sc_String); + } + facecount++; + } + + if (SC_Compare("}") && (facecount == 3 || facecount == 6)) + { + int i; + int ok = true; + + for (i = 0; i < facecount; i++) + { + if (R_CheckTextureNumForName(sb.faces[i]) == -1) + { + ok = false; + break; + } + } + + if (ok) + { + BoxSkyboxCount++; + BoxSkybox = realloc(BoxSkybox, BoxSkyboxCount * sizeof(BoxSkybox[0])); + memcpy(&BoxSkybox[BoxSkyboxCount - 1], &sb, sizeof(sb)); + } + } + } + } + + R_SetBoxSkybox(skytexture); +} + +int gld_BindFace(box_skybox_t *sb, int index) +{ + int lump; + GLTexture *gltexture; + char *name = sb->faces[index]; + +#if 0 + lump = W_CheckNumForName(name); + if (lump != -1) + { + gltexture = gld_RegisterPatch(lump, CR_DEFAULT, false); + gltexture->wrap_mode = GLEXT_CLAMP_TO_EDGE; + gld_BindPatch(gltexture, CR_DEFAULT); + return true; + } + + //lump = R_FlatNumForName(name); + lump = (W_CheckNumForName)(name, ns_flats); + if (lump != -1) + { + lump -= firstflat; + gltexture = gld_RegisterFlat(lump, true); + gltexture->wrap_mode = GLEXT_CLAMP_TO_EDGE; + gld_BindFlat(gltexture); + return true; + } +#endif + + lump = R_CheckTextureNumForName(name); + if (lump != -1) + { + gltexture = gld_RegisterTexture(lump, false, false); + gld_BindTexture(gltexture, GLTEXTURE_CLAMPXY); + return true; + } + + return false; +} + +int gld_DrawBoxSkyBox(void) +{ + int faces; + box_skybox_t *sb; + + if (BoxSkyboxCount == 0) + return false; + + if (SkyBox.side) + { + sb = R_GetBoxSkybox(SkyBox.side->skybox_index); + } + else + { + sb = BoxSkybox_default; + } + + if (!sb) + { + return false; + } + + gld_DrawFakeSkyStrips(); + + glDepthMask(false); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_ALPHA_TEST); + + SetTextureMode(TM_OPAQUE); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + glLoadIdentity(); + + glRotatef(roll, 0.0f, 0.0f, 1.0f); + glRotatef(pitch, 1.0f, 0.0f, 0.0f); + glRotatef(yaw, 0.0f, 1.0f, 0.0f); + glScalef(-2.0f, 2.0f, 2.0f); + + if (SkyBox.side) + { + float xoffset = (float)SkyBox.side->textureoffset * 180.0f / (float)ANG180; + glRotatef(-180.0f + xoffset, 0.0f, 1.0f, 0.0f); + } + + if (sb->faces[5][0]) + { + faces = 4; + + // north + gld_BindFace(sb, 0); + + glBegin(GL_TRIANGLE_FAN); + glTexCoord2f(0.0f, 0.0f); + glVertex3f(+MAXCOORD, MAXCOORD, -MAXCOORD); + glTexCoord2f(1.0f, 0.0f); + glVertex3f(-MAXCOORD, +MAXCOORD, -MAXCOORD); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(-MAXCOORD, -MAXCOORD, -MAXCOORD); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(+MAXCOORD, -MAXCOORD, -MAXCOORD); + glEnd(); + + // east + gld_BindFace(sb, 1); + + glBegin(GL_TRIANGLE_FAN); + glTexCoord2f(0.0f, 0.0f); + glVertex3f(-MAXCOORD, +MAXCOORD, -MAXCOORD); + glTexCoord2f(1.0f, 0.0f); + glVertex3f(-MAXCOORD, +MAXCOORD, +MAXCOORD); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(-MAXCOORD, -MAXCOORD, +MAXCOORD); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(-MAXCOORD, -MAXCOORD, -MAXCOORD); + glEnd(); + + // south + gld_BindFace(sb, 2); + + glBegin(GL_TRIANGLE_FAN); + glTexCoord2f(0.0f, 0.0f); + glVertex3f(-MAXCOORD, +MAXCOORD, +MAXCOORD); + glTexCoord2f(1.0f, 0.0f); + glVertex3f(+MAXCOORD, +MAXCOORD, +MAXCOORD); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(+MAXCOORD, -MAXCOORD, +MAXCOORD); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(-MAXCOORD, -MAXCOORD, +MAXCOORD); + glEnd(); + + // west + gld_BindFace(sb, 3); + + glBegin(GL_TRIANGLE_FAN); + glTexCoord2f(0.0f, 0.0f); + glVertex3f(+MAXCOORD, +MAXCOORD, +MAXCOORD); + glTexCoord2f(1.0f, 0.0f); + glVertex3f(+MAXCOORD, +MAXCOORD, -MAXCOORD); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(+MAXCOORD, -MAXCOORD, -MAXCOORD); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(+MAXCOORD, -MAXCOORD, +MAXCOORD); + glEnd(); + } + else + { + faces = 1; + + // all 4 sides + gld_BindFace(sb, 0); + + glBegin(GL_TRIANGLE_FAN); + glTexCoord2f(0.0f, 0.0f); + glVertex3f(+MAXCOORD, +MAXCOORD, -MAXCOORD); + glTexCoord2f(.25f, 0); + glVertex3f(-MAXCOORD, +MAXCOORD, -MAXCOORD); + glTexCoord2f(.25f, 1); + glVertex3f(-MAXCOORD, -MAXCOORD, -MAXCOORD); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(+MAXCOORD, -MAXCOORD, -MAXCOORD); + glEnd(); + + // east + glBegin(GL_TRIANGLE_FAN); + glTexCoord2f(.25f, 0); + glVertex3f(-MAXCOORD, +MAXCOORD, -MAXCOORD); + glTexCoord2f(.5f, 0); + glVertex3f(-MAXCOORD, +MAXCOORD, +MAXCOORD); + glTexCoord2f(.5f, 1); + glVertex3f(-MAXCOORD, -MAXCOORD, +MAXCOORD); + glTexCoord2f(.25f, 1); + glVertex3f(-MAXCOORD, -MAXCOORD, -MAXCOORD); + glEnd(); + + // south + glBegin(GL_TRIANGLE_FAN); + glTexCoord2f(.5f, 0); + glVertex3f(-MAXCOORD, +MAXCOORD, +MAXCOORD); + glTexCoord2f(.75f, 0); + glVertex3f(+MAXCOORD, +MAXCOORD, +MAXCOORD); + glTexCoord2f(.75f, 1); + glVertex3f(+MAXCOORD, -MAXCOORD, +MAXCOORD); + glTexCoord2f(.5f, 1); + glVertex3f(-MAXCOORD, -MAXCOORD, +MAXCOORD); + glEnd(); + + // west + glBegin(GL_TRIANGLE_FAN); + glTexCoord2f(.75f, 0); + glVertex3f(+MAXCOORD, +MAXCOORD, +MAXCOORD); + glTexCoord2f(1.0f, 0.0f); + glVertex3f(+MAXCOORD, +MAXCOORD, -MAXCOORD); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(+MAXCOORD, -MAXCOORD, -MAXCOORD); + glTexCoord2f(.75f, 1); + glVertex3f(+MAXCOORD, -MAXCOORD, +MAXCOORD); + glEnd(); + } + + // top + gld_BindFace(sb, faces); + glBegin(GL_TRIANGLE_FAN); + if (!sb->fliptop) + { + glTexCoord2f(0.0f, 0.0f); + glVertex3f(+MAXCOORD, +MAXCOORD, -MAXCOORD); + glTexCoord2f(1.0f, 0.0f); + glVertex3f(-MAXCOORD, +MAXCOORD, -MAXCOORD); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(-MAXCOORD, +MAXCOORD, +MAXCOORD); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(+MAXCOORD, +MAXCOORD, +MAXCOORD); + } + else + { + glTexCoord2f(0.0f, 0.0f); + glVertex3f(+MAXCOORD, +MAXCOORD, +MAXCOORD); + glTexCoord2f(1.0f, 0.0f); + glVertex3f(-MAXCOORD, +MAXCOORD, +MAXCOORD); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(-MAXCOORD, +MAXCOORD, -MAXCOORD); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(+MAXCOORD, +MAXCOORD, -MAXCOORD); + } + glEnd(); + + // bottom + gld_BindFace(sb, faces + 1); + + glBegin(GL_TRIANGLE_FAN); + glTexCoord2f(0.0f, 0.0f); + glVertex3f(+MAXCOORD, -MAXCOORD, -MAXCOORD); + glTexCoord2f(1.0f, 0.0f); + glVertex3f(-MAXCOORD, -MAXCOORD, -MAXCOORD); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(-MAXCOORD, -MAXCOORD, +MAXCOORD); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(+MAXCOORD, -MAXCOORD, +MAXCOORD); + glEnd(); + + glPopMatrix(); + + glEnable(GL_ALPHA_TEST); + glEnable(GL_DEPTH_TEST); + glDepthMask(true); + + SetFrameTextureMode(); + + return true; +} diff --git a/src/gl_struct.h b/src/gl_struct.h new file mode 100644 index 0000000..b02da64 --- /dev/null +++ b/src/gl_struct.h @@ -0,0 +1,301 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifndef _GL_STRUCT_H +#define _GL_STRUCT_H + +#include + +extern int nodesVersion; + +typedef enum { + filter_nearest, + filter_linear, + filter_nearest_mipmap_nearest, + filter_nearest_mipmap_linear, + filter_linear_mipmap_nearest, + filter_linear_mipmap_linear, + + filter_count +} filter_texture_mode_t; + +typedef enum { + skytype_auto, + skytype_none, + skytype_standard, + skytype_skydome, + skytype_screen, + + skytype_count +} skytype_t; + +#define MAX_GLGAMMA 32 +typedef enum +{ + gl_lightmode_glboom, + gl_lightmode_gzdoom, + gl_lightmode_fogbased, + gl_lightmode_shaders, + + gl_lightmode_last +} gl_lightmode_t; + +extern int gl_skymode; +extern int gl_drawskys; +extern int gl_hardware_gamma; +extern gl_lightmode_t gl_lightmode; +extern gl_lightmode_t gl_lightmode_default; +extern const char *gl_lightmodes[]; +extern int gl_light_ambient; +extern int useglgamma; +int gld_SetGammaRamp(int gamma); +void gld_CheckHardwareGamma(void); +void gld_FlushTextures(void); +void gld_ApplyGammaRamp(byte *buf, int pitch, int width, int height); +void M_ChangeLightMode(void); + +//detail +extern int gl_detail_maxdist; +extern int gl_allow_detail_textures; + +void gld_InitVertexData(); +void gld_CleanVertexData(); +void gld_UpdateSplitData(sector_t *sector); + +extern int gl_boom_colormaps; +extern int gl_boom_colormaps_default; + +void gld_Init(int width, int height); +void gld_InitCommandLine(void); +void gld_InitTextureParams(void); + +void gld_DrawNumPatch(int x, int y, int lump, int cm, enum patch_translation_e flags); +void gld_DrawNumPatch_f(float x, float y, int lump, int cm, enum patch_translation_e flags); + +void gld_FillFlat(int lump, int x, int y, int width, int height, enum patch_translation_e flags); +#define gld_FillFlatName(flatname, x, y, width, height, flags) \ + gld_FillFlat(R_FlatNumForName(flatname), (x), (y), (width), (height), (flags)) + +void gld_FillPatch(int lump, int x, int y, int width, int height, enum patch_translation_e flags); +#define gld_FillPatchName(name, x, y, width, height, flags) \ + gld_FillPatch(W_GetNumForName(name), (x), (y), (width), (height), (flags)) + +void gld_DrawLine(int x0, int y0, int x1, int y1, int BaseColor); +void gld_DrawLine_f(float x0, float y0, float x1, float y1, int BaseColor); +void gld_DrawWeapon(int weaponlump, vissprite_t *vis, int lightlevel); +void gld_FillBlock(int x, int y, int width, int height, int col); +void gld_SetPalette(int palette); +unsigned char *gld_ReadScreen(void); + +void gld_CleanMemory(void); +void gld_CleanStaticMemory(void); +void gld_PreprocessLevel(void); + +void gld_Set2DMode(); +void gld_InitDrawScene(void); +void gld_StartDrawScene(void); +void gld_AddPlane(int subsectornum, visplane_t *floor, visplane_t *ceiling); +void gld_AddWall(seg_t *seg); +void gld_ProjectSprite(mobj_t* thing, int lightlevel); +void gld_DrawScene(player_t *player); +void gld_EndDrawScene(void); +void gld_ProcessExtraAlpha(void); +void gld_Finish(); + +//stuff +extern int gl_color_mip_levels; + +//blend animation from zdoomgl +extern int gl_blend_animations; + +// wipe +int gld_wipe_doMelt(int ticks, int *y_lookup); +int gld_wipe_exitMelt(int ticks); +int gld_wipe_StartScreen(void); +int gld_wipe_EndScreen(void); + +// hires +extern int gl_hires_24bit_colormap; +extern int gl_texture_external_hires; +extern int gl_texture_internal_hires; +extern int gl_hires_override_pwads; +extern const char *gl_texture_hires_dir; +int gld_PrecacheGUIPatches(void); + +//HQ resize +typedef enum +{ + hq_scale_none, + hq_scale_2x, + hq_scale_3x, + hq_scale_4x, + + hq_scale_max +} gl_hqresizemode_t; +extern const char *gl_hqresizemodes[]; +extern int gl_texture_hqresize; +extern int gl_texture_hqresize_textures; +extern int gl_texture_hqresize_sprites; +extern int gl_texture_hqresize_patches; + +//clipper +dboolean gld_clipper_SafeCheckRange(angle_t startAngle, angle_t endAngle); +void gld_clipper_SafeAddClipRange(angle_t startangle, angle_t endangle); +void gld_clipper_SafeAddClipRangeRealAngles(angle_t startangle, angle_t endangle); +void gld_clipper_Clear(void); +angle_t gld_FrustumAngle(void); +void gld_FrustrumSetup(void); +dboolean gld_SphereInFrustum(float x, float y, float z, float radius); + +//missing flats (fake floors and ceilings) +extern dboolean gl_use_stencil; +sector_t* GetBestFake(sector_t *sector, int ceiling, int validcount); +sector_t* GetBestBleedSector(sector_t* source, int ceiling); + +//shadows +typedef struct shadow_params_s +{ + int enable; + int loaded; + + GLuint tex_id; + int width; + int height; + + int max_radius; + int max_dist; + float factor; + float bias; +} simple_shadow_params_t; + +extern simple_shadow_params_t simple_shadows; +extern int gl_shadows_maxdist; +extern int gl_shadows_factor; + +void gld_DrawMapLines(void); + +//sprites +typedef enum { spriteclip_const, spriteclip_always, spriteclip_smart } spriteclipmode_t; +typedef enum { fuzz_darken, fuzz_shadow, fuzz_transparent, fuzz_ghostly, fuzz_last } spritefuzzmode_t; +extern spritefuzzmode_t gl_thingspritefuzzmode; +extern spritefuzzmode_t gl_weaponspritefuzzmode; +extern const char *gl_spritefuzzmodes[]; +extern spriteclipmode_t gl_spriteclip; +extern const char *gl_spriteclipmodes[]; +extern int gl_spriteclip_threshold; +extern float gl_spriteclip_threshold_f; +extern int gl_sprites_frustum_culling; +extern int gl_sprite_offset_default; +extern float gl_sprite_offset; +extern int gl_sprite_blend; +extern int gl_mask_sprite_threshold; + +//skybox +int R_BoxSkyboxNumForName(const char *name); +void R_SetBoxSkybox(int texture); + +//multisampling +void gld_MultisamplingInit(void); +void gld_MultisamplingCheck(void); +void gld_MultisamplingSet(void); + +//display lists +extern int gl_use_display_lists; + +void gld_ProcessTexturedMap(void); +void gld_ResetTexturedAutomap(void); +void gld_MapDrawSubsectors(player_t *plr, int fx, int fy, fixed_t mx, fixed_t my, int fw, int fh, fixed_t scale); + +void gld_Init8InGLMode(void); +void gld_Draw8InGL(void); + +//motion blur +typedef struct motion_blur_params_s +{ + int enabled; + + const char *str_min_angle; + const char *str_min_speed; + const char *str_att_a; + const char *str_att_b; + const char *str_att_c; + + float curr_speed_pow2; + float minspeed_pow2; + int minangle; + float att_a; + float att_b; + float att_c; +} motion_blur_params_t; + +extern int gl_use_motionblur; +extern motion_blur_params_t motion_blur; + +// Nice map +enum +{ + am_icon_shadow, + + am_icon_corpse, + am_icon_normal, + am_icon_health, + am_icon_armor, + am_icon_ammo, + am_icon_key, + am_icon_power, + am_icon_weap, + + am_icon_arrow, + am_icon_monster, + am_icon_player, + am_icon_mark, + am_icon_bullet, + + am_icon_count +}; + +typedef struct am_icon_s +{ + GLuint tex_id; + const char* name; + int lumpnum; +} am_icon_t; +extern am_icon_t am_icons[]; + +void gld_InitMapPics(void); +void gld_AddNiceThing(int type, float x, float y, float radius, float angle, + unsigned char r, unsigned char g, unsigned char b, unsigned char a); +void gld_DrawNiceThings(int fx, int fy, int fw, int fh); +void gld_ClearNiceThings(void); + +#endif // _GL_STRUCT_H diff --git a/src/gl_texture.c b/src/gl_texture.c new file mode 100644 index 0000000..f844fa0 --- /dev/null +++ b/src/gl_texture.c @@ -0,0 +1,1600 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gl_opengl.h" + +#include "z_zone.h" +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#endif +#ifndef CALLBACK +#define CALLBACK +#endif +#include +#include +#include +#include "doomtype.h" +#include "w_wad.h" +#include "m_argv.h" +#include "d_event.h" +#include "v_video.h" +#include "doomstat.h" +#include "r_bsp.h" +#include "r_main.h" +#include "r_draw.h" +#include "r_sky.h" +#include "r_plane.h" +#include "r_data.h" +#include "p_maputl.h" +#include "p_tick.h" +#include "m_bbox.h" +#include "lprintf.h" +#include "gl_intern.h" +#include "gl_struct.h" +#include "p_spec.h" +#include "e6y.h" + +int imageformats[5] = {0, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA}; + +/* TEXTURES */ +static GLTexture **gld_GLTextures=NULL; +/* PATCHES FLATS SPRITES */ +static GLTexture **gld_GLPatchTextures=NULL; +static GLTexture **gld_GLStaticPatchTextures=NULL; + +tex_filter_t tex_filter[MIP_COUNT]; + +const char *gl_tex_format_string; +//int gl_tex_format=GL_RGBA8; +int gl_tex_format=GL_RGB5_A1; +//int gl_tex_format=GL_RGBA4; +//int gl_tex_format=GL_RGBA2; + +// e6y: development aid to see texture mip usage +int gl_color_mip_levels; + +int gl_boom_colormaps = -1; +int gl_boom_colormaps_default; + +GLuint* last_glTexID = NULL; + +int transparent_pal_index; +unsigned char gld_palmap[256]; + +void gld_ResetLastTexture(void) +{ + last_glTexID = NULL; +} + +void gld_InitPalettedTextures(void) +{ + const unsigned char *playpal; + int pal[256]; + int i,j; + + playpal = V_GetPlaypal(); + for (i=0; i<256; i++) { + pal[i] = (playpal[i*3+0] << 16) | (playpal[i*3+1] << 8) | playpal[i*3+2]; + gld_palmap[i] = i; + } + transparent_pal_index = -1; + for (i=0; i<256; i++) { + for (j=i+1; j<256; j++) { + if (pal[i] == pal[j]) { + transparent_pal_index = j; + gld_palmap[j] = i; + break; + } + } + if (transparent_pal_index >= 0) + break; + } +} + +int gld_GetTexDimension(int value) +{ + int i; + + if (value > gl_max_texture_size) + value = gl_max_texture_size; + + if (gl_arb_texture_non_power_of_two) + return value; + + i = 1; + while (i < value) + i += i; + + return i; +} + +// e6y +// Creates TIntDynArray +void* NewIntDynArray(int dimCount, int *dims) +{ + int i, dim; + int tableOffset; + int bufferSize = 0; + int tableSize = 1; + void* buffer; + + for(dim = 0; dim < dimCount - 1; dim++) + { + tableSize *= dims[dim]; + bufferSize += sizeof(int*) * tableSize; + } + + bufferSize += sizeof(int) * tableSize * dims[dimCount - 1]; + + buffer = calloc(1, bufferSize); + if(!buffer) + { + return 0; + } + + tableOffset = 0; + tableSize = 1; + + for(dim = 0; dim < dimCount - 1; dim++) + { + tableSize *= dims[dim]; + tableOffset += tableSize; + + for(i = 0; i < tableSize; i++) + { + if(dim < dimCount - 2) + { + *((int***)buffer + tableOffset - tableSize + i) = + (((int**)buffer + tableOffset + i*dims[dim + 1])); + } + else + { + *((int**)buffer + tableOffset - tableSize + i) = + ((int*)((int**)buffer + tableOffset) + i*dims[dim + 1]); + } + } + } + + return buffer; +} + +// e6y +// Get index of player->fixedcolormap for GLTexture().glTexExID array +// There are three known values for player->fixedcolormap: 0, 1 and 32 +// 0 (normal) -> 0; 1 (pw_infrared) -> 1; 32 (pw_invulnerability) -> 2 +void gld_GetTextureTexID(GLTexture *gltexture, int cm) +{ + static int data[NUMCOLORMAPS+1] = { + 0, 1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 2 + }; + + gltexture->cm = cm; + gltexture->player_cm = 0; + + if (!gl_boom_colormaps) + { + gltexture->texflags_p = &gltexture->texflags[cm][0]; + gltexture->texid_p = &gltexture->glTexExID[cm][0][0]; + return; + } + + if (!(gltexture->flags & GLTEXTURE_HIRES)) + { + gltexture->player_cm = data[frame_fixedcolormap]; + } + + gltexture->texflags_p = &gltexture->texflags[cm][gltexture->player_cm]; + gltexture->texid_p = &gltexture->glTexExID[cm][gltexture->player_cm][boom_cm]; + return; +} + +// e6y +// The common function for adding textures and patches +// Used by gld_AddNewGLTexture and gld_AddNewGLPatchTexture +static GLTexture *gld_AddNewGLTexItem(int num, int count, GLTexture ***items) +{ + if (num<0) + return NULL; + if (num>=count) + return NULL; + if (!(*items)) + { + (*items)=Z_Calloc(count, sizeof(GLTexture *),PU_STATIC,0); + } + if (!(*items)[num]) + { + (*items)[num]=Z_Calloc(1, sizeof(GLTexture),PU_STATIC,0); + (*items)[num]->textype=GLDT_UNREGISTERED; + + //if (gl_boom_colormaps) + { + GLTexture *texture = (*items)[num]; + int dims[3] = {(CR_LIMIT+MAXPLAYERS), (PLAYERCOLORMAP_COUNT), numcolormaps}; + texture->glTexExID = NewIntDynArray(3, dims); + } + } + return (*items)[num]; +} + +static GLTexture *gld_AddNewGLTexture(int texture_num) +{ + return gld_AddNewGLTexItem(texture_num, numtextures, &gld_GLTextures); +} + +static GLTexture *gld_AddNewGLPatchTexture(int lump) +{ + if (lumpinfo[lump].flags & LUMP_STATIC) + return gld_AddNewGLTexItem(lump, numlumps, &gld_GLStaticPatchTextures); + else + return gld_AddNewGLTexItem(lump, numlumps, &gld_GLPatchTextures); +} + +void gld_SetTexturePalette(GLenum target) +{ + const unsigned char *playpal; + unsigned char pal[1024]; + int i; + + playpal = V_GetPlaypal(); + for (i=0; i<256; i++) { + pal[i*4+0] = playpal[i*3+0]; + pal[i*4+1] = playpal[i*3+1]; + pal[i*4+2] = playpal[i*3+2]; + pal[i*4+3] = 255; + } + pal[transparent_pal_index*4+0]=0; + pal[transparent_pal_index*4+1]=0; + pal[transparent_pal_index*4+2]=0; + pal[transparent_pal_index*4+3]=0; + GLEXT_glColorTableEXT(target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, pal); +} + +static void gld_AddPatchToTexture_UnTranslated(GLTexture *gltexture, unsigned char *buffer, const rpatch_t *patch, int originx, int originy, int paletted) +{ + int x,y,j; + int xs,xe; + int js,je; + const rcolumn_t *column; + const byte *source; + int i, pos; + const unsigned char *playpal; + + if (!gltexture) + return; + if (!patch) + return; + playpal = V_GetPlaypal(); + xs=0; + xe=patch->width; + if ((xs+originx)>=gltexture->realtexwidth) + return; + if ((xe+originx)<=0) + return; + if ((xs+originx)<0) + xs=-originx; + if ((xe+originx)>gltexture->realtexwidth) + xe+=(gltexture->realtexwidth-(xe+originx)); + + //e6y + if (patch->flags&PATCH_HASHOLES) + gltexture->flags |= GLTEXTURE_HASHOLES; + + for (x=xs;x=patch->width) + { + lprintf(LO_ERROR,"gld_AddPatchToTexture_UnTranslated x>=patch->width (%i >= %i)\n",x,patch->width); + return; + } +#endif + column = &patch->columns[x]; + for (i=0; inumPosts; i++) { + const rpost_t *post = &column->posts[i]; + y=(post->topdelta+originy); + js=0; + je=post->length; + if ((js+y)>=gltexture->realtexheight) + continue; + if ((je+y)<=0) + continue; + if ((js+y)<0) + js=-y; + if ((je+y)>gltexture->realtexheight) + je+=(gltexture->realtexheight-(je+y)); + source = column->pixels + post->topdelta; + if (paletted) { + pos=(((js+y)*gltexture->buffer_width)+x+originx); + for (j=js;jbuffer_width)) + { +#ifdef RANGECHECK + if (pos>=gltexture->buffer_size) + { + lprintf(LO_ERROR,"gld_AddPatchToTexture_UnTranslated pos>=size (%i >= %i)\n",pos+3,gltexture->buffer_size); + return; + } +#endif + buffer[pos]=gld_palmap[source[j]]; + } + } else { + pos=4*(((js+y)*gltexture->buffer_width)+x+originx); + for (j=js;jbuffer_width)) + { +#ifdef RANGECHECK + if ((pos+3)>=gltexture->buffer_size) + { + lprintf(LO_ERROR,"gld_AddPatchToTexture_UnTranslated pos+3>=size (%i >= %i)\n",pos+3,gltexture->buffer_size); + return; + } +#endif + //e6y: Boom's color maps + if (gl_boom_colormaps && use_boom_cm && !(comp[comp_skymap] && (gltexture->flags&GLTEXTURE_SKY))) + { + const lighttable_t *colormap = (fixedcolormap ? fixedcolormap : fullcolormap); + buffer[pos+0]=playpal[colormap[source[j]]*3+0]; + buffer[pos+1]=playpal[colormap[source[j]]*3+1]; + buffer[pos+2]=playpal[colormap[source[j]]*3+2]; + } + else + { + buffer[pos+0]=playpal[source[j]*3+0]; + buffer[pos+1]=playpal[source[j]*3+1]; + buffer[pos+2]=playpal[source[j]*3+2]; + } + buffer[pos+3]=255; + } + } + } + } +} + +void gld_AddPatchToTexture(GLTexture *gltexture, unsigned char *buffer, const rpatch_t *patch, int originx, int originy, int cm, int paletted) +{ + int x,y,j; + int xs,xe; + int js,je; + const rcolumn_t *column; + const byte *source; + int i, pos; + const unsigned char *playpal; + const unsigned char *outr; + + if (!gltexture) + return; + if (!patch) + return; + if ((cm==CR_DEFAULT) || (cm==CR_LIMIT)) + { + gld_AddPatchToTexture_UnTranslated(gltexture,buffer,patch,originx,originy, paletted); + return; + } + if (cmwidth; + if ((xs+originx)>=gltexture->realtexwidth) + return; + if ((xe+originx)<=0) + return; + if ((xs+originx)<0) + xs=-originx; + if ((xe+originx)>gltexture->realtexwidth) + xe+=(gltexture->realtexwidth-(xe+originx)); + + //e6y + if (patch->flags&PATCH_HASHOLES) + gltexture->flags |= GLTEXTURE_HASHOLES; + + for (x=xs;x=patch->width) + { + lprintf(LO_ERROR,"gld_AddPatchToTexture x>=patch->width (%i >= %i)\n",x,patch->width); + return; + } +#endif + column = &patch->columns[x]; + for (i=0; inumPosts; i++) { + const rpost_t *post = &column->posts[i]; + y=(post->topdelta+originy); + js=0; + je=post->length; + if ((js+y)>=gltexture->realtexheight) + continue; + if ((je+y)<=0) + continue; + if ((js+y)<0) + js=-y; + if ((je+y)>gltexture->realtexheight) + je+=(gltexture->realtexheight-(je+y)); + source = column->pixels + post->topdelta; + if (paletted) { + pos=(((js+y)*gltexture->buffer_width)+x+originx); + for (j=js;jbuffer_width)) + { +#ifdef RANGECHECK + if (pos>=gltexture->buffer_size) + { + lprintf(LO_ERROR,"gld_AddPatchToTexture_UnTranslated pos>=size (%i >= %i)\n",pos+3,gltexture->buffer_size); + return; + } +#endif + buffer[pos]=gld_palmap[outr[source[j]]]; + } + } else { + pos=4*(((js+y)*gltexture->buffer_width)+x+originx); + for (j=js;jbuffer_width)) + { +#ifdef RANGECHECK + if ((pos+3)>=gltexture->buffer_size) + { + lprintf(LO_ERROR,"gld_AddPatchToTexture pos+3>=size (%i >= %i)\n",pos+3,gltexture->buffer_size); + return; + } +#endif + //e6y: Boom's color maps + if (gl_boom_colormaps && use_boom_cm) + { + const lighttable_t *colormap = (fixedcolormap ? fixedcolormap : fullcolormap); + buffer[pos+0]=playpal[colormap[outr[source[j]]]*3+0]; + buffer[pos+1]=playpal[colormap[outr[source[j]]]*3+1]; + buffer[pos+2]=playpal[colormap[outr[source[j]]]*3+2]; + } + else + { + buffer[pos+0]=playpal[outr[source[j]]*3+0]; + buffer[pos+1]=playpal[outr[source[j]]*3+1]; + buffer[pos+2]=playpal[outr[source[j]]*3+2]; + } + buffer[pos+3]=255; + } + } + } + } +} + +static void gld_AddFlatToTexture(GLTexture *gltexture, unsigned char *buffer, const unsigned char *flat, int paletted) +{ + int x,y,pos; + const unsigned char *playpal; + + if (!gltexture) + return; + if (!flat) + return; + if (paletted) { + for (y=0;yrealtexheight;y++) + { + pos=(y*gltexture->buffer_width); + for (x=0;xrealtexwidth;x++,pos++) + { +#ifdef RANGECHECK + if (pos>=gltexture->buffer_size) + { + lprintf(LO_ERROR,"gld_AddFlatToTexture pos>=size (%i >= %i)\n",pos,gltexture->buffer_size); + return; + } +#endif + buffer[pos]=gld_palmap[flat[y*64+x]]; + } + } + } else { + playpal = V_GetPlaypal(); + for (y=0;yrealtexheight;y++) + { + pos=4*(y*gltexture->buffer_width); + for (x=0;xrealtexwidth;x++,pos+=4) + { +#ifdef RANGECHECK + if ((pos+3)>=gltexture->buffer_size) + { + lprintf(LO_ERROR,"gld_AddFlatToTexture pos+3>=size (%i >= %i)\n",pos+3,gltexture->buffer_size); + return; + } +#endif + //e6y: Boom's color maps + if (gl_boom_colormaps && use_boom_cm) + { + const lighttable_t *colormap = (fixedcolormap ? fixedcolormap : fullcolormap); + buffer[pos+0]=playpal[colormap[flat[y*64+x]]*3+0]; + buffer[pos+1]=playpal[colormap[flat[y*64+x]]*3+1]; + buffer[pos+2]=playpal[colormap[flat[y*64+x]]*3+2]; + } + else + { + buffer[pos+0]=playpal[flat[y*64+x]*3+0]; + buffer[pos+1]=playpal[flat[y*64+x]*3+1]; + buffer[pos+2]=playpal[flat[y*64+x]*3+2]; + } + buffer[pos+3]=255; + } + } + } +} + +//e6y: "force" flag for loading texture with zero index +GLTexture *gld_RegisterTexture(int texture_num, dboolean mipmap, dboolean force) +{ + GLTexture *gltexture; + + //e6y: textures with zero index should be loaded sometimes + if (texture_num==NO_TEXTURE && !force) + return NULL; + gltexture=gld_AddNewGLTexture(texture_num); + if (!gltexture) + return NULL; + if (gltexture->textype==GLDT_UNREGISTERED) + { + texture_t *texture=NULL; + + if ((texture_num>=0) || (texture_numtextype=GLDT_BROKEN; + gltexture->index=texture_num; + + //e6y + gltexture->flags = 0; + if (mipmap && tex_filter[MIP_TEXTURE].mipmap) + gltexture->flags |= GLTEXTURE_MIPMAP; + + gltexture->realtexwidth=texture->width; + gltexture->realtexheight=texture->height; + gltexture->leftoffset=0; + gltexture->topoffset=0; + gltexture->tex_width=gld_GetTexDimension(gltexture->realtexwidth); + gltexture->tex_height=gld_GetTexDimension(gltexture->realtexheight); + gltexture->width=MIN(gltexture->realtexwidth, gltexture->tex_width); + gltexture->height=MIN(gltexture->realtexheight, gltexture->tex_height); + gltexture->buffer_width=gltexture->tex_width; + gltexture->buffer_height=gltexture->tex_height; +#ifdef USE_GLU_IMAGESCALE + gltexture->width=gltexture->tex_width; + gltexture->height=gltexture->tex_height; + gltexture->buffer_width=gltexture->realtexwidth; + gltexture->buffer_height=gltexture->realtexheight; +#endif + if (gltexture->flags & GLTEXTURE_MIPMAP) + { + gltexture->width=gltexture->tex_width; + gltexture->height=gltexture->tex_height; + gltexture->buffer_width=gltexture->realtexwidth; + gltexture->buffer_height=gltexture->realtexheight; + } + + //e6y: right/bottom UV coordinates for texture drawing + gltexture->scalexfac=(float)gltexture->width/(float)gltexture->tex_width; + gltexture->scaleyfac=(float)gltexture->height/(float)gltexture->tex_height; + + gltexture->buffer_size=gltexture->buffer_width*gltexture->buffer_height*4; + if (gltexture->realtexwidth>gltexture->buffer_width) + return gltexture; + if (gltexture->realtexheight>gltexture->buffer_height) + return gltexture; + + gltexture->textype=GLDT_TEXTURE; + + gld_SetTexDetail(gltexture); + } + return gltexture; +} + +unsigned char* gld_GetTextureBuffer(GLuint texid, int miplevel, int *width, int *height) +{ + int w, h; + static unsigned char *buf = NULL; + static int buf_size = 512 * 256 * 4; + + if (!buf) + { + buf = malloc(buf_size); + } + + if (texid) + { + glBindTexture(GL_TEXTURE_2D, texid); + } + + glGetTexLevelParameteriv(GL_TEXTURE_2D, miplevel, GL_TEXTURE_WIDTH, &w); + glGetTexLevelParameteriv(GL_TEXTURE_2D, miplevel, GL_TEXTURE_HEIGHT, &h); + if (w * h * 4 > buf_size) + { + free(buf); + buf_size = w * h * 4; + buf = malloc(buf_size); + } + glGetTexImage(GL_TEXTURE_2D, miplevel, GL_RGBA, GL_UNSIGNED_BYTE, buf); + + if (width) + *width = w; + if (height) + *height = h; + + return buf; +} + +// e6y: from Quake3 +// R_BlendOverTexture +// Apply a color blend over a set of pixels +static void gld_BlendOverTexture(byte *data, int pixelCount, byte blend[4]) +{ + int i; + int inverseAlpha; + int premult[3]; + + inverseAlpha = 255 - blend[3]; + premult[0] = blend[0] * blend[3]; + premult[1] = blend[1] * blend[3]; + premult[2] = blend[2] * blend[3]; + + for(i = 0; i < pixelCount; i++, data += 4) + { + data[0] = (data[0] * inverseAlpha + premult[0]) >> 9; + data[1] = (data[1] * inverseAlpha + premult[1]) >> 9; + data[2] = (data[2] * inverseAlpha + premult[2]) >> 9; + } +} + +byte mipBlendColors[16][4] = +{ + {0,0,0,0}, + {255,0,0,128}, + {0,255,0,128}, + {0,0,255,128}, + {255,0,0,128}, + {0,255,0,128}, + {0,0,255,128}, + {255,0,0,128}, + {0,255,0,128}, + {0,0,255,128}, + {255,0,0,128}, + {0,255,0,128}, + {0,0,255,128}, + {255,0,0,128}, + {0,255,0,128}, + {0,0,255,128}, +}; + +static void gld_RecolorMipLevels(byte *data) +{ + //e6y: development aid to see texture mip usage + if (gl_color_mip_levels) + { + int miplevel = 0; + unsigned char *buf = NULL; + + for (miplevel = 1; miplevel < 16; miplevel++) + { + int w, h; + + buf = gld_GetTextureBuffer(0, miplevel, &w, &h); + + if (w <= 0 || h <= 0) + break; + + gld_BlendOverTexture((byte *)buf, w * h, mipBlendColors[miplevel]); + glTexImage2D( GL_TEXTURE_2D, miplevel, gl_tex_format, w, h, + 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); + } + } +} + +void gld_SetTexFilters(GLTexture *gltexture) +{ + int mip, mag_filter, min_filter; + float aniso_filter = 0.0f; + + switch (gltexture->textype) + { + case GLDT_TEXTURE: + case GLDT_FLAT: + mip = MIP_TEXTURE; + break; + case GLDT_PATCH: + mip = ((gltexture->flags & GLTEXTURE_SPRITE) ? MIP_SPRITE : MIP_PATCH); + break; + default: + mip = MIP_TEXTURE; + break; + } + + if (render_usedetail && gltexture->detail) + mag_filter = GL_LINEAR; + else + mag_filter = tex_filter[mip].mag_filter; + + if ((gltexture->flags & GLTEXTURE_MIPMAP) && tex_filter[mip].mipmap) + { + min_filter = tex_filter[mip].min_filter; + if (gl_ext_texture_filter_anisotropic) + aniso_filter = (GLfloat)(1< 0.0f) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso_filter); +} + +void gld_SetTexClamp(GLTexture *gltexture, unsigned int flags) +{ + //if ((gltexture->flags & GLTEXTURE_CLAMPXY) != (flags & GLTEXTURE_CLAMPXY)) + /* sp1n0za 05/2010: simplify */ + if ((*gltexture->texflags_p ^ flags) & GLTEXTURE_CLAMPXY) + { + int need_clamp_x = (flags & GLTEXTURE_CLAMPX); + int need_clamp_y = (flags & GLTEXTURE_CLAMPY); + int has_clamp_x = (*gltexture->texflags_p & GLTEXTURE_CLAMPX); + int has_clamp_y = (*gltexture->texflags_p & GLTEXTURE_CLAMPY); + + if (need_clamp_x) + { + if (!has_clamp_x) + { + *gltexture->texflags_p |= GLTEXTURE_CLAMPX; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GLEXT_CLAMP_TO_EDGE); + } + } + else + { + if (has_clamp_x) + { + *gltexture->texflags_p &= ~GLTEXTURE_CLAMPX; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + } + } + + if (need_clamp_y) + { + if (!has_clamp_y) + { + *gltexture->texflags_p |= GLTEXTURE_CLAMPY; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GLEXT_CLAMP_TO_EDGE); + } + } + else + { + if (has_clamp_y) + { + *gltexture->texflags_p &= ~GLTEXTURE_CLAMPY; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + } + } +} + +int gld_BuildTexture(GLTexture *gltexture, void *data, dboolean readonly, int width, int height) +{ + int result = false; + + int tex_width, tex_height, tex_buffer_size; + unsigned char *tex_buffer = NULL; + + tex_width = gld_GetTexDimension(width); + tex_height = gld_GetTexDimension(height); + tex_buffer_size = tex_width * tex_height * 4; + + //your video is modern + if (gl_arb_texture_non_power_of_two) + { + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, + ((gltexture->flags & GLTEXTURE_MIPMAP) ? GL_TRUE : GL_FALSE)); + + glTexImage2D( GL_TEXTURE_2D, 0, gl_tex_format, + tex_width, tex_height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + + gld_RecolorMipLevels(data); + + gld_SetTexFilters(gltexture); + + result = true; + goto l_exit; + } + +#ifdef USE_GLU_MIPMAP + if (gltexture->flags & GLTEXTURE_MIPMAP) + { + gluBuild2DMipmaps(GL_TEXTURE_2D, gl_tex_format, + width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); + + gld_RecolorMipLevels(data); + + gld_SetTexFilters(gltexture); + + result = true; + goto l_exit; + } + else +#endif // USE_GLU_MIPMAP + { +#ifdef USE_GLU_IMAGESCALE + if ((width != tex_width) || (height != tex_height)) + { + tex_buffer = malloc(tex_buffer_size); + if (!tex_buffer) + { + goto l_exit; + } + + gluScaleImage(GL_RGBA, width, height, + GL_UNSIGNED_BYTE, data, + tex_width, tex_height, + GL_UNSIGNED_BYTE, tex_buffer); + + glTexImage2D( GL_TEXTURE_2D, 0, gl_tex_format, + tex_width, tex_height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_buffer); + } + else +#endif // USE_GLU_IMAGESCALE + { + if ((width != tex_width) || (height != tex_height)) + { + if (width == tex_width) + { + tex_buffer = malloc(tex_buffer_size); + memcpy(tex_buffer, data, width * height * 4); + } + else + { + int y; + tex_buffer = calloc(1, tex_buffer_size); + for (y = 0; y < height; y++) + { + memcpy(tex_buffer + y * tex_width * 4, + ((unsigned char*)data) + y * width * 4, width * 4); + } + } + } + else + { + tex_buffer = data; + } + + if (gl_paletted_texture) { + gld_SetTexturePalette(GL_TEXTURE_2D); + glTexImage2D( GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, + tex_width, tex_height, + 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, tex_buffer); + } else { + glTexImage2D( GL_TEXTURE_2D, 0, gl_tex_format, + tex_width, tex_height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_buffer); + } + } + + gltexture->flags &= ~GLTEXTURE_MIPMAP; + gld_SetTexFilters(gltexture); + result = true; + } + +l_exit: + if (result) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + + if (tex_buffer && tex_buffer != data) + { + free(tex_buffer); + tex_buffer = NULL; + } + + if (!readonly) + { + free(data); + data = NULL; + } + + return result; +} + +void gld_BindTexture(GLTexture *gltexture, unsigned int flags) +{ + const rpatch_t *patch; + unsigned char *buffer; + int w, h; + + if (!gltexture || gltexture->textype != GLDT_TEXTURE) + { + glBindTexture(GL_TEXTURE_2D, 0); + last_glTexID = NULL; + return; + } + +#ifdef HAVE_LIBSDL2_IMAGE + if (gld_LoadHiresTex(gltexture, CR_DEFAULT)) + { + gld_SetTexClamp(gltexture, flags); + last_glTexID = gltexture->texid_p; + return; + } +#endif + + gld_GetTextureTexID(gltexture, CR_DEFAULT); + + if (last_glTexID == gltexture->texid_p) + { + gld_SetTexClamp(gltexture, flags); + return; + } + + last_glTexID = gltexture->texid_p; + + if (*gltexture->texid_p != 0) + { + glBindTexture(GL_TEXTURE_2D, *gltexture->texid_p); + gld_SetTexClamp(gltexture, flags); + return; + } + + buffer=(unsigned char*)Z_Malloc(gltexture->buffer_size,PU_STATIC,0); + if (!(gltexture->flags & GLTEXTURE_MIPMAP) && gl_paletted_texture) + memset(buffer,transparent_pal_index,gltexture->buffer_size); + else + memset(buffer,0,gltexture->buffer_size); + patch=R_CacheTextureCompositePatchNum(gltexture->index); + gld_AddPatchToTexture(gltexture, buffer, patch, 0, 0, + CR_DEFAULT, !(gltexture->flags & GLTEXTURE_MIPMAP) && gl_paletted_texture); + R_UnlockTextureCompositePatchNum(gltexture->index); + if (*gltexture->texid_p == 0) + glGenTextures(1, gltexture->texid_p); + glBindTexture(GL_TEXTURE_2D, *gltexture->texid_p); + + if (gltexture->flags & GLTEXTURE_HASHOLES) + { + SmoothEdges(buffer, gltexture->buffer_width, gltexture->buffer_height); + } + + buffer = gld_HQResize(gltexture, buffer, gltexture->buffer_width, gltexture->buffer_height, &w, &h); + + gld_BuildTexture(gltexture, buffer, false, w, h); + + gld_SetTexClamp(gltexture, flags); +} + +GLTexture *gld_RegisterPatch(int lump, int cm, dboolean is_sprite) +{ + const rpatch_t *patch; + GLTexture *gltexture; + + gltexture=gld_AddNewGLPatchTexture(lump); + if (!gltexture) + return NULL; + if (gltexture->textype==GLDT_UNREGISTERED) + { + patch=R_CachePatchNum(lump); + if (!patch) + return NULL; + gltexture->textype=GLDT_BROKEN; + gltexture->index=lump; + + //e6y + gltexture->flags = 0; + if (is_sprite) + { + gltexture->flags |= GLTEXTURE_SPRITE; + if (tex_filter[MIP_SPRITE].mipmap) + gltexture->flags |= GLTEXTURE_MIPMAP; + } + else + { + if (tex_filter[MIP_PATCH].mipmap) + gltexture->flags |= GLTEXTURE_MIPMAP; + } + //gltexture->wrap_mode = (patch->flags & PATCH_REPEAT ? GL_REPEAT : GLEXT_CLAMP_TO_EDGE); + + gltexture->realtexwidth=patch->width; + gltexture->realtexheight=patch->height; + gltexture->leftoffset=patch->leftoffset; + gltexture->topoffset=patch->topoffset; + gltexture->tex_width=gld_GetTexDimension(gltexture->realtexwidth); + gltexture->tex_height=gld_GetTexDimension(gltexture->realtexheight); + gltexture->width=MIN(gltexture->realtexwidth, gltexture->tex_width); + gltexture->height=MIN(gltexture->realtexheight, gltexture->tex_height); + gltexture->buffer_width=gltexture->tex_width; + gltexture->buffer_height=gltexture->tex_height; +#ifdef USE_GLU_IMAGESCALE + gltexture->width=MIN(gltexture->realtexwidth, gltexture->tex_width); + gltexture->height=MIN(gltexture->realtexheight, gltexture->tex_height); + gltexture->buffer_width=MAX(gltexture->realtexwidth, gltexture->tex_width); + gltexture->buffer_height=MAX(gltexture->realtexheight, gltexture->tex_height); +#endif + if (gltexture->flags & GLTEXTURE_MIPMAP) + { + gltexture->width=gltexture->tex_width; + gltexture->height=gltexture->tex_height; + gltexture->buffer_width=gltexture->realtexwidth; + gltexture->buffer_height=gltexture->realtexheight; + } + + //e6y: right/bottom UV coordinates for patch drawing + gltexture->scalexfac=(float)gltexture->width/(float)gltexture->tex_width; + gltexture->scaleyfac=(float)gltexture->height/(float)gltexture->tex_height; + + gltexture->buffer_size=gltexture->buffer_width*gltexture->buffer_height*4; + R_UnlockPatchNum(lump); + if (gltexture->realtexwidth>gltexture->buffer_width) + return gltexture; + if (gltexture->realtexheight>gltexture->buffer_height) + return gltexture; + gltexture->textype=GLDT_PATCH; + } + return gltexture; +} + +void gld_BindPatch(GLTexture *gltexture, int cm) +{ + const rpatch_t *patch; + unsigned char *buffer; + int w, h; + + if (!gltexture || gltexture->textype != GLDT_PATCH) + { + glBindTexture(GL_TEXTURE_2D, 0); + last_glTexID = NULL; + return; + } + +#ifdef HAVE_LIBSDL2_IMAGE + if (gld_LoadHiresTex(gltexture, cm)) + { + gld_SetTexClamp(gltexture, GLTEXTURE_CLAMPXY); + last_glTexID = gltexture->texid_p; + return; + } +#endif + + gld_GetTextureTexID(gltexture, cm); + + if (last_glTexID == gltexture->texid_p) + { + gld_SetTexClamp(gltexture, GLTEXTURE_CLAMPXY); + return; + } + + last_glTexID = gltexture->texid_p; + + if (*gltexture->texid_p != 0) + { + glBindTexture(GL_TEXTURE_2D, *gltexture->texid_p); + gld_SetTexClamp(gltexture, GLTEXTURE_CLAMPXY); + return; + } + + patch=R_CachePatchNum(gltexture->index); + buffer=(unsigned char*)Z_Malloc(gltexture->buffer_size,PU_STATIC,0); + if (gl_paletted_texture) + memset(buffer,transparent_pal_index,gltexture->buffer_size); + else + memset(buffer,0,gltexture->buffer_size); + gld_AddPatchToTexture(gltexture, buffer, patch, 0, 0, cm, gl_paletted_texture); + + // e6y + // Post-process the texture data after the buffer has been created. + // Smooth the edges of transparent fields in the texture. + // + // It is a workaround to set the color of all transparent pixels + // that border on a non-transparent pixel to the color + // of one bordering non-transparent pixel. + // It is necessary for textures that are not power of two + // to avoid the lines (boxes) around the elements that change + // on the intermission screens in Doom1 (E2, E3) + +// if ((gltexture->flags & (GLTEXTURE_HASHOLES | GLTEXTURE_SPRITE)) == +// (GLTEXTURE_HASHOLES | GLTEXTURE_SPRITE)) + if ((gltexture->flags & GLTEXTURE_HASHOLES)) + { + SmoothEdges(buffer, gltexture->buffer_width, gltexture->buffer_height); + } + + if (*gltexture->texid_p == 0) + glGenTextures(1, gltexture->texid_p); + glBindTexture(GL_TEXTURE_2D, *gltexture->texid_p); + + buffer = gld_HQResize(gltexture, buffer, gltexture->buffer_width, gltexture->buffer_height, &w, &h); + + gld_BuildTexture(gltexture, buffer, false, w, h); + + gld_SetTexClamp(gltexture, GLTEXTURE_CLAMPXY); + + R_UnlockPatchNum(gltexture->index); +} + +GLTexture *gld_RegisterFlat(int lump, dboolean mipmap) +{ + GLTexture *gltexture; + + gltexture=gld_AddNewGLPatchTexture(firstflat+lump); + if (!gltexture) + return NULL; + if (gltexture->textype==GLDT_UNREGISTERED) + { + gltexture->textype=GLDT_BROKEN; + gltexture->index=firstflat+lump; + + //e6y + gltexture->flags = 0; + if (mipmap && tex_filter[MIP_TEXTURE].mipmap) + gltexture->flags |= GLTEXTURE_MIPMAP; + + gltexture->realtexwidth=64; + gltexture->realtexheight=64; + gltexture->leftoffset=0; + gltexture->topoffset=0; + gltexture->tex_width=gld_GetTexDimension(gltexture->realtexwidth); + gltexture->tex_height=gld_GetTexDimension(gltexture->realtexheight); + gltexture->width=MIN(gltexture->realtexwidth, gltexture->tex_width); + gltexture->height=MIN(gltexture->realtexheight, gltexture->tex_height); + gltexture->buffer_width=gltexture->tex_width; + gltexture->buffer_height=gltexture->tex_height; +#ifdef USE_GLU_IMAGESCALE + gltexture->width=gltexture->tex_width; + gltexture->height=gltexture->tex_height; + gltexture->buffer_width=gltexture->realtexwidth; + gltexture->buffer_height=gltexture->realtexheight; +#endif + if (gltexture->flags & GLTEXTURE_MIPMAP) + { + gltexture->width=gltexture->tex_width; + gltexture->height=gltexture->tex_height; + gltexture->buffer_width=gltexture->realtexwidth; + gltexture->buffer_height=gltexture->realtexheight; + } + + //e6y: right/bottom UV coordinates for flat drawing + gltexture->scalexfac=(float)gltexture->width/(float)gltexture->tex_width; + gltexture->scaleyfac=(float)gltexture->height/(float)gltexture->tex_height; + + gltexture->buffer_size=gltexture->buffer_width*gltexture->buffer_height*4; + if (gltexture->realtexwidth>gltexture->buffer_width) + return gltexture; + if (gltexture->realtexheight>gltexture->buffer_height) + return gltexture; + + gltexture->textype=GLDT_FLAT; + + gld_SetTexDetail(gltexture); + } + return gltexture; +} + +void gld_BindFlat(GLTexture *gltexture, unsigned int flags) +{ + const unsigned char *flat; + unsigned char *buffer; + int w, h; + + if (!gltexture || gltexture->textype != GLDT_FLAT) + { + glBindTexture(GL_TEXTURE_2D, 0); + last_glTexID = NULL; + return; + } + +#ifdef HAVE_LIBSDL2_IMAGE + if (gld_LoadHiresTex(gltexture, CR_DEFAULT)) + { + gld_SetTexClamp(gltexture, flags); + last_glTexID = gltexture->texid_p; + return; + } +#endif + + gld_GetTextureTexID(gltexture, CR_DEFAULT); + + if (last_glTexID == gltexture->texid_p) + { + gld_SetTexClamp(gltexture, flags); + return; + } + + last_glTexID = gltexture->texid_p; + + if (*gltexture->texid_p != 0) + { + glBindTexture(GL_TEXTURE_2D, *gltexture->texid_p); + gld_SetTexClamp(gltexture, flags); + return; + } + + flat=W_CacheLumpNum(gltexture->index); + buffer=(unsigned char*)Z_Malloc(gltexture->buffer_size,PU_STATIC,0); + if (!(gltexture->flags & GLTEXTURE_MIPMAP) && gl_paletted_texture) + memset(buffer,transparent_pal_index,gltexture->buffer_size); + else + memset(buffer,0,gltexture->buffer_size); + gld_AddFlatToTexture(gltexture, buffer, flat, !(gltexture->flags & GLTEXTURE_MIPMAP) && gl_paletted_texture); + if (*gltexture->texid_p == 0) + glGenTextures(1, gltexture->texid_p); + glBindTexture(GL_TEXTURE_2D, *gltexture->texid_p); + + buffer = gld_HQResize(gltexture, buffer, gltexture->buffer_width, gltexture->buffer_height, &w, &h); + + gld_BuildTexture(gltexture, buffer, false, w, h); + + gld_SetTexClamp(gltexture, flags); + + W_UnlockLumpNum(gltexture->index); +} + +// e6y +// The common function for cleaning textures and patches +// gld_CleanTextures and gld_CleanPatchTextures are replaced with that +static void gld_CleanTexItems(int count, GLTexture ***items) +{ + int i, j; + + if (!(*items)) + return; + + for (i=0; iglTexExID[j][n][cm]) + { + glDeleteTextures(1,&((*items)[i]->glTexExID[j][n][cm])); + } + } + } + } + + Z_Free((*items)[i]->glTexExID); + (*items)[i]->glTexExID = NULL; + + Z_Free((*items)[i]); + } + } + memset((*items),0,count*sizeof(GLTexture *)); +} + +void gld_FlushTextures(void) +{ + gld_CleanTexItems(numtextures, &gld_GLTextures); + gld_CleanTexItems(numlumps, &gld_GLPatchTextures); + gld_CleanTexItems(numlumps, &gld_GLStaticPatchTextures); + + gl_has_hires = 0; + + gld_ResetLastTexture(); +#ifdef HAVE_LIBSDL2_IMAGE + gld_HiRes_BuildTables(); +#endif + + gld_InitSky(); + + // do not draw anything in current frame after flushing + gld_ResetDrawInfo(); +} + +static void CalcHitsCount(const byte *hitlist, int size, int *hit, int*hitcount) +{ + int i; + + if (!hitlist || !hit || !hitcount) + return; + + *hit = 0; + *hitcount = 0; + for (i = 0; i < size; i++) + { + if (hitlist[i]) + (*hitcount)++; + } +} + +void gld_Precache(void) +{ + int i; + byte *hitlist; + int hit, hitcount = 0; + GLTexture *gltexture; + box_skybox_t *sb; + + unsigned int tics = SDL_GetTicks(); + + int usehires = (gl_texture_external_hires) || + (gl_texture_internal_hires && r_have_internal_hires); + + if (doSkip || nodrawers) + return; + + if (!usehires) + { + if (!precache) + return; + + if (timingdemo) + return; + } + + gld_ProgressStart(); + + { + size_t size = numflats > numsprites ? numflats : numsprites; + hitlist = Z_Malloc((size_t)numtextures > size ? (size_t)numtextures : size,PU_LEVEL,0); + } + + // Precache flats. + + memset(hitlist, 0, numflats); + + for (i = numsectors; --i >= 0; ) + { + int j,k; + + int floorpic = sectors[i].floorpic; + int ceilingpic = sectors[i].ceilingpic; + + anim_t *flatanims[2] = { + anim_flats[floorpic].anim, + anim_flats[ceilingpic].anim + }; + + hitlist[floorpic] = hitlist[ceilingpic] = 1; + + //e6y: animated flats + for (k = 0; k < 2; k++) + { + if (flatanims[k] && !flatanims[k]->istexture) + { + for (j = flatanims[k]->basepic; j < flatanims[k]->basepic + flatanims[k]->numpics; j++) + hitlist[j] = 1; + } + } + } + + CalcHitsCount(hitlist, numflats, &hit, &hitcount); + + for (i = numflats; --i >= 0; ) + if (hitlist[i]) + { + gld_ProgressUpdate("Loading Flats...", ++hit, hitcount); + gltexture = gld_RegisterFlat(i,true); + if (gltexture) + { + gld_BindFlat(gltexture, 0); + } + } + + // Precache textures. + + memset(hitlist, 0, numtextures); + + for (i = numsides; --i >= 0;) + { + int j, k; + int bottomtexture, toptexture, midtexture; + anim_t *textureanims[3]; + + if (sides[i].special == 271 || sides[i].special == 272) + { + sb = R_GetBoxSkybox(sides[i].skybox_index); + if (sb) + { + int texture; + int face = 0; + while (face < 6 && sb->faces[face]) + { + texture = R_CheckTextureNumForName(sb->faces[face]); + if (texture != -1) + { + hitlist[texture] = 1; + } + face++; + } + } + } + + bottomtexture = sides[i].bottomtexture; + toptexture = sides[i].toptexture; + midtexture = sides[i].midtexture; + + textureanims[0] = anim_textures[bottomtexture].anim; + textureanims[1] = anim_textures[toptexture].anim; + textureanims[2] = anim_textures[midtexture].anim; + + hitlist[bottomtexture] = + hitlist[toptexture] = + hitlist[midtexture] = 1; + + //e6y: animated textures + for (k = 0; k < 3; k++) + { + if (textureanims[k] && textureanims[k]->istexture) + { + for (j = textureanims[k]->basepic; j < textureanims[k]->basepic + textureanims[k]->numpics; j++) + hitlist[j] = 1; + } + } + + //e6y: swithes + { + int GetPairForSwitchTexture(side_t *side); + int pair = GetPairForSwitchTexture(&sides[i]); + if (pair != -1) + hitlist[pair] = 1; + } + } + + // Sky texture is always present. + // Note that F_SKY1 is the name used to + // indicate a sky floor/ceiling as a flat, + // while the sky texture is stored like + // a wall texture, with an episode dependend + // name. + + if (hitlist) + hitlist[skytexture] = usehires ? 1 : 0; + + sb = BoxSkybox_default; + if (sb) + { + int texture; + int face = 0; + while (face < 6 && sb->faces[face]) + { + texture = R_CheckTextureNumForName(sb->faces[face]); + if (texture != -1) + { + hitlist[texture] = 1; + } + face++; + } + } + + CalcHitsCount(hitlist, numtextures, &hit, &hitcount); + + for (i = numtextures; --i >= 0; ) + if (hitlist[i]) + { + gld_ProgressUpdate("Loading Textures...", ++hit, hitcount); + gltexture = gld_RegisterTexture(i, i != skytexture, false); + if (gltexture) + { + gld_BindTexture(gltexture, 0); + } + } + + // Precache sprites. + memset(hitlist, 0, numsprites); + + if (thinkercap.next) + { + thinker_t *th; + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + { + if (th->function == P_MobjThinker) + hitlist[((mobj_t *)th)->sprite] = 1; + } + } + + hit = 0; + hitcount = 0; + for (i = 0; i < numsprites; i++) + { + if (hitlist[i]) + hitcount += 7 * sprites[i].numframes; + } + + for (i=numsprites; --i >= 0;) + if (hitlist[i]) + { + int j = sprites[i].numframes; + while (--j >= 0) + { + short *sflump = sprites[i].spriteframes[j].lump; + int k = 7; + do + { + gld_ProgressUpdate("Loading Sprites...", ++hit, hitcount); + gltexture = gld_RegisterPatch(firstspritelump + sflump[k], CR_LIMIT, true); + if (gltexture) + { + gld_BindPatch(gltexture, CR_LIMIT); + } + } + while (--k >= 0); + } + } + Z_Free(hitlist); + + if (gl_texture_external_hires) + { +#ifdef HAVE_LIBSDL2_IMAGE + gld_PrecacheGUIPatches(); +#endif + } + + gld_ProgressEnd(); + +#ifdef USE_FBO_TECHNIQUE + gld_InitFBO(); +#endif + + // e6y: some statistics. make sense for hires + { + char map[8]; + + if (gamemode == commercial) + sprintf(map, "MAP%02i", gamemap); + else + sprintf(map, "E%iM%i", gameepisode, gamemap); + + lprintf(LO_INFO, "gld_Precache: %s done in %d ms\n", map, SDL_GetTicks() - tics); + } +} + +void gld_CleanMemory(void) +{ + gld_CleanVertexData(); + gld_CleanTexItems(numtextures, &gld_GLTextures); + gld_CleanTexItems(numlumps, &gld_GLPatchTextures); + gld_CleanDisplayLists(); + gl_preprocessed = false; +} + +void gld_CleanStaticMemory(void) +{ + gld_CleanTexItems(numlumps, &gld_GLStaticPatchTextures); +} diff --git a/src/gl_vertex.c b/src/gl_vertex.c new file mode 100644 index 0000000..47437a0 --- /dev/null +++ b/src/gl_vertex.c @@ -0,0 +1,481 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +/* +** gl_vertex.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2006 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +// Adapted for prboom(-plus) by entryway + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gl_opengl.h" + +#include "z_zone.h" +#include "v_video.h" +#include "gl_intern.h" +#include "r_main.h" + +typedef struct vertexsplit_info_s +{ + dboolean changed; + int numheights; + int numsectors; + sector_t **sectors; + float *heightlist; + byte validcount; +} vertexsplit_info_t; + +static vertexsplit_info_t * gl_vertexsplit = NULL; + +typedef struct splitsbysector_s +{ + int numsplits; + vertexsplit_info_t **splits; +} splitsbysector_t; +static splitsbysector_t * gl_splitsbysector = NULL; + +//========================================================================== +// +// Split left edge of wall +// +//========================================================================== +void gld_SplitLeftEdge(const GLWall *wall, dboolean detail) +{ + vertex_t *v; + vertexsplit_info_t *vi; + + v = wall->seg->linedef->v1; + + if (v == NULL) + return; + + vi = &gl_vertexsplit[v - vertexes]; + + if (vi->numheights) + { + int i = 0; + + float polyh1 = wall->ytop - wall->ybottom; + float factv1 = (polyh1 ? (wall->vt - wall->vb) / polyh1 : 0); + float factu1 = (polyh1 ? (wall->ul - wall->ul) / polyh1 : 0); + + detail = detail && wall->gltexture->detail; + + while (i < vi->numheights && vi->heightlist[i] <= wall->ybottom) + i++; + + while (i < vi->numheights && vi->heightlist[i] < wall->ytop) + { + GLTexture *tex = wall->gltexture; + GLfloat s = factu1 * (vi->heightlist[i] - wall->ytop) + wall->ul; + GLfloat t = factv1 * (vi->heightlist[i] - wall->ytop) + wall->vt; + + if (detail) + { + if (gl_arb_multitexture) + { + GLEXT_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, s, t); + GLEXT_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, + s * tex->detail_width + tex->detail->offsetx, + t * tex->detail_height + tex->detail->offsety); + } + else + { + glTexCoord2f( + s * tex->detail_width + tex->detail->offsetx, + t * tex->detail_height + tex->detail->offsety); + } + } + else + { + glTexCoord2f(s, t); + } + glVertex3f(wall->glseg->x1, vi->heightlist[i], wall->glseg->z1); + i++; + } + } +} + +//========================================================================== +// +// Split right edge of wall +// +//========================================================================== +void gld_SplitRightEdge(const GLWall *wall, dboolean detail) +{ + vertex_t *v; + vertexsplit_info_t * vi; + + v = wall->seg->linedef->v2; + + if (v == NULL) + return; + + vi = &gl_vertexsplit[v - vertexes]; + + if (vi->numheights) + { + int i = vi->numheights - 1; + + float polyh2 = wall->ytop - wall->ybottom; + float factv2 = (polyh2 ? (wall->vt - wall->vb) / polyh2 : 0); + float factu2 = (polyh2 ? (wall->ur - wall->ur) / polyh2 : 0); + + detail = detail && wall->gltexture->detail; + + while (i > 0 && vi->heightlist[i] >= wall->ytop) + i--; + + while (i > 0 && vi->heightlist[i] > wall->ybottom) + { + GLTexture *tex = wall->gltexture; + GLfloat s = factu2 * (vi->heightlist[i] - wall->ytop) + wall->ur; + GLfloat t = factv2 * (vi->heightlist[i] - wall->ytop) + wall->vt; + + if (detail) + { + if (gl_arb_multitexture) + { + GLEXT_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, s, t); + GLEXT_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, + s * tex->detail_width + tex->detail->offsetx, + t * tex->detail_height + tex->detail->offsety); + } + else + { + glTexCoord2f( + s * tex->detail_width + tex->detail->offsetx, + t * tex->detail_height + tex->detail->offsety); + } + } + else + { + glTexCoord2f(s, t); + } + glVertex3f(wall->glseg->x2, vi->heightlist[i], wall->glseg->z2); + i--; + } + } +} + +//========================================================================== +// +// Recalculate all heights affectting this vertex. +// +//========================================================================== +void gld_RecalcVertexHeights(const vertex_t *v) +{ + extern byte rendermarker; + + int i,j,k; + float height; + vertexsplit_info_t *vi; + + vi = &gl_vertexsplit[v - vertexes]; + + if (vi->validcount == rendermarker) + return; + + vi->validcount = rendermarker; + + if (!vi->changed) + return; + + vi->changed = false; + + vi->numheights = 0; + for(i = 0; i < vi->numsectors; i++) + { + for(j = 0; j < 2; j++) + { + if (j == 0) + height = (float)vi->sectors[i]->ceilingheight/MAP_SCALE+SMALLDELTA; + else + height = (float)vi->sectors[i]->floorheight/MAP_SCALE-SMALLDELTA; + + for(k = 0; k < vi->numheights; k++) + { + if (height == vi->heightlist[k]) + break; + + if (height < vi->heightlist[k]) + { + memmove(&vi->heightlist[k + 1], &vi->heightlist[k], sizeof(vi->heightlist[0]) * (vi->numheights - k)); + vi->heightlist[k] = height; + vi->numheights++; + break; + } + } + if (k == vi->numheights) + vi->heightlist[vi->numheights++] = height; + } + } + + if (vi->numheights <= 2) + vi->numheights = 0; // is not in need of any special attention +} + +//========================================================================== +// +// +// +//========================================================================== +static void AddToVertex(const sector_t *sec, int **list, unsigned int *size) +{ + unsigned int i; + int secno = sec->iSectorID; + + for(i = 0; i < (*size); i++) + { + if ((*list)[i] == secno) + return; + } + (*list) = realloc((*list), sizeof(*list) * ((*size) + 1)); + (*list)[(*size)] = secno; + (*size)++; +} + +//========================================================================== +// +// +// +//========================================================================== +static void AddToSplitBySector(vertexsplit_info_t *vi, splitsbysector_t *splitsbysector) +{ + int i; + for(i = 0; i < splitsbysector->numsplits; i++) + { + if (splitsbysector->splits[i] == vi) + return; + } + splitsbysector->splits = realloc( + splitsbysector->splits, + sizeof(splitsbysector->splits) * (splitsbysector->numsplits + 1)); + splitsbysector->splits[splitsbysector->numsplits] = vi; + splitsbysector->numsplits++; +} + +//========================================================================== +// +// +// +//========================================================================== +void gld_InitVertexData() +{ + int i, j, k; + int vertexes_count, gl_vertexsplit_size, pos; + int ** vt_sectorlists; + unsigned int * vt_sectorlists_size; + + if (gl_vertexsplit) + return; + + vt_sectorlists = calloc(sizeof(vt_sectorlists[0]), numvertexes); + vt_sectorlists_size = calloc(sizeof(vt_sectorlists_size[0]), numvertexes); + + for(i = 0; i < numlines; i++) + { + line_t *line = &lines[i]; + + for(j = 0; j < 2; j++) + { + vertex_t *v = (j==0 ? line->v1 : line->v2); + + for(k = 0; k < 2; k++) + { + sector_t *sec = (k == 0 ? line->frontsector : line->backsector); + + if (sec) + { + AddToVertex(sec, &vt_sectorlists[v-vertexes], &vt_sectorlists_size[v-vertexes]); + if (sec->heightsec >= 0) + { + AddToVertex(§ors[sec->heightsec], &vt_sectorlists[v-vertexes], &vt_sectorlists_size[v-vertexes]); + } + } + } + } + } + + vertexes_count = 0; + for(i = 0; i < numvertexes; i++) + { + if (vt_sectorlists_size[i] > 1) + { + vertexes_count += vt_sectorlists_size[i]; + } + } + + gl_vertexsplit_size = + numvertexes * sizeof(vertexsplit_info_t) + + vertexes_count * sizeof(gl_vertexsplit->sectors[0]) + + 2 * vertexes_count * sizeof(gl_vertexsplit->heightlist[0]); + + gl_vertexsplit = malloc(gl_vertexsplit_size); + memset(gl_vertexsplit, 0, gl_vertexsplit_size); + + pos = numvertexes * sizeof(vertexsplit_info_t); + for(i = 0; i < numvertexes; i++) + { + int cnt = vt_sectorlists_size[i]; + vertexsplit_info_t *vi = &gl_vertexsplit[i]; + + vi->validcount = -1; + vi->numheights = 0; + if (cnt > 1) + { + vi->changed = true; + vi->numsectors = cnt; + + vi->sectors = (sector_t **)((unsigned char*)gl_vertexsplit + pos); + pos += sizeof(vi->sectors[0]) * cnt; + vi->heightlist = (float *)((unsigned char*)gl_vertexsplit + pos); + pos += sizeof(vi->heightlist[0]) * cnt * 2; + + for(j = 0; j < cnt; j++) + { + vi->sectors[j] = §ors[vt_sectorlists[i][j]]; + } + } + else + { + vi->numsectors=0; + } + } + + gl_splitsbysector = malloc(sizeof(gl_splitsbysector[0]) * numsectors); + memset(gl_splitsbysector, 0, sizeof(gl_splitsbysector[0]) * numsectors); + + for(i = 0; i < numsectors; i++) + { + for(j = 0; j < numvertexes; j++) + { + vertexsplit_info_t *vi = &gl_vertexsplit[j]; + + for(k = 0; k < vi->numsectors; k++) + { + if (vi->sectors[k] == §ors[i]) + AddToSplitBySector(vi, &gl_splitsbysector[i]); + } + } + } + + for(i = 0; i < numvertexes; i++) + gld_RecalcVertexHeights(&vertexes[i]); + + free(vt_sectorlists); + free(vt_sectorlists_size); +} + +//========================================================================== +// +// +// +//========================================================================== +void gld_UpdateSplitData(sector_t *sector) +{ + int i; + + if (gl_splitsbysector) + { + splitsbysector_t *splitsbysector = &gl_splitsbysector[sector->iSectorID]; + for (i = 0; i < splitsbysector->numsplits; i++) + { + splitsbysector->splits[i]->changed = true; + } + } +} + +//========================================================================== +// +// +// +//========================================================================== +void gld_CleanVertexData() +{ + if (gl_vertexsplit) + { + free(gl_vertexsplit); + gl_vertexsplit = NULL; + } + + if (gl_splitsbysector) + { + int i; + for(i = 0; i < numsectors; i++) + { + if (gl_splitsbysector[i].numsplits > 0) + { + free(gl_splitsbysector[i].splits); + } + } + free(gl_splitsbysector); + gl_splitsbysector = NULL; + } +} diff --git a/src/gl_wipe.c b/src/gl_wipe.c new file mode 100644 index 0000000..ea8597c --- /dev/null +++ b/src/gl_wipe.c @@ -0,0 +1,153 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gl_opengl.h" + +#include "v_video.h" +#include "gl_intern.h" +#include "m_random.h" +#include "lprintf.h" +#include "e6y.h" + +static GLuint wipe_scr_start_tex = 0; +static GLuint wipe_scr_end_tex = 0; + +GLuint CaptureScreenAsTexID(void) +{ + GLuint id; + + gld_EnableTexture2D(GL_TEXTURE0_ARB, true); + + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, + gld_GetTexDimension(SCREENWIDTH), gld_GetTexDimension(SCREENHEIGHT), + 0, GL_RGB, GL_UNSIGNED_BYTE, 0); + + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, SCREENWIDTH, SCREENHEIGHT); + + return id; +} + +int gld_wipe_doMelt(int ticks, int *y_lookup) +{ + int i; + int total_w, total_h; + float fU1, fU2, fV1, fV2; + + total_w = gld_GetTexDimension(SCREENWIDTH); + total_h = gld_GetTexDimension(SCREENHEIGHT); + + fU1 = 0.0f; + fV1 = (float)SCREENHEIGHT / (float)total_h; + fU2 = (float)SCREENWIDTH / (float)total_w; + fV2 = 0.0f; + + gld_EnableTexture2D(GL_TEXTURE0_ARB, true); + + glBindTexture(GL_TEXTURE_2D, wipe_scr_end_tex); + glColor3f(1.0f, 1.0f, 1.0f); + + glBegin(GL_TRIANGLE_STRIP); + { + glTexCoord2f(fU1, fV1); glVertex2f(0.0f, 0.0f); + glTexCoord2f(fU1, fV2); glVertex2f(0.0f, (float)SCREENHEIGHT); + glTexCoord2f(fU2, fV1); glVertex2f((float)SCREENWIDTH, 0.0f); + glTexCoord2f(fU2, fV2); glVertex2f((float)SCREENWIDTH, (float)SCREENHEIGHT); + } + glEnd(); + + glBindTexture(GL_TEXTURE_2D, wipe_scr_start_tex); + glColor3f(1.0f, 1.0f, 1.0f); + + glBegin(GL_QUAD_STRIP); + + for (i=0; i <= SCREENWIDTH; i++) + { + int yoffs = MAX(0, y_lookup[i]); + + float tx = (float) i / total_w; + float sx = (float) i; + float sy = (float) yoffs; + + glTexCoord2f(tx, fV1); glVertex2f(sx, sy); + glTexCoord2f(tx, fV2); glVertex2f(sx, sy + (float)SCREENHEIGHT); + } + + glEnd(); + + return 0; +} + +int gld_wipe_exitMelt(int ticks) +{ + if (wipe_scr_start_tex != 0) + { + glDeleteTextures(1, &wipe_scr_start_tex); + wipe_scr_start_tex = 0; + } + if (wipe_scr_end_tex != 0) + { + glDeleteTextures(1, &wipe_scr_end_tex); + wipe_scr_end_tex = 0; + } + + gld_ResetLastTexture(); + + return 0; +} + +int gld_wipe_StartScreen(void) +{ + wipe_scr_start_tex = CaptureScreenAsTexID(); + + return 0; +} + +int gld_wipe_EndScreen(void) +{ + glFlush(); + wipe_scr_end_tex = CaptureScreenAsTexID(); + + return 0; +} diff --git a/src/hu_lib.c b/src/hu_lib.c new file mode 100644 index 0000000..45b3b8a --- /dev/null +++ b/src/hu_lib.c @@ -0,0 +1,823 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: heads-up text and input code + * + *----------------------------------------------------------------------------- + */ + +#include "doomdef.h" +#include "doomstat.h" +#include "v_video.h" +#include "hu_lib.h" +#include "hu_stuff.h" +#include "r_main.h" +#include "r_draw.h" + +extern int key_backspace; // phares +extern int key_enter; // phares + +// +// not used currently +// code to initialize HUlib would go here if needed +// +static void HUlib_init(void) +{ +} + +//////////////////////////////////////////////////////// +// +// Basic text line widget +// +//////////////////////////////////////////////////////// + +// +// HUlib_clearTextLine() +// +// Blank the internal text line in a hu_textline_t widget +// +// Passed a hu_textline_t, returns nothing +// +void HUlib_clearTextLine(hu_textline_t* t) +{ + t->w = 0; + t->linelen = // killough 1/23 98: support multiple lines + t->len = 0; + t->l[0] = 0; + t->needsupdate = true; +} + +// +// HUlib_initTextLine() +// +// Initialize a hu_textline_t widget. Set the position, font, start char +// of the font, and color range to be used. +// +// Passed a hu_textline_t, and the values used to initialize +// Returns nothing +// +void HUlib_initTextLine(hu_textline_t* t, int x, int y, + const patchnum_t* f, int sc, int cm, enum patch_translation_e flags ) + //jff 2/16/98 add color range parameter +{ + t->x = x; + t->y = y; + t->val = -1; + t->f = f; + t->sc = sc; + t->cm = cm; + t->flags = flags; + HUlib_clearTextLine(t); +} + +// +// HUlib_addCharToTextLine() +// +// Adds a character at the end of the text line in a hu_textline_t widget +// +// Passed the hu_textline_t and the char to add +// Returns false if already at length limit, true if the character added +// +dboolean HUlib_addCharToTextLine +( hu_textline_t* t, + char ch ) +{ + // killough 1/23/98 -- support multiple lines + if (t->linelen == HU_MAXLINELENGTH) + return false; + else + { + t->linelen++; + if (ch == '\n') + t->linelen=0; + + t->l[t->len++] = ch; + t->l[t->len] = 0; + t->needsupdate = 4; + return true; + } + +} + +// +// HUlib_delCharFromTextLine() +// +// Deletes a character at the end of the text line in a hu_textline_t widget +// +// Passed the hu_textline_t +// Returns false if already empty, true if the character deleted +// +static dboolean HUlib_delCharFromTextLine(hu_textline_t* t) +{ + if (!t->len) return false; + else + { + t->l[--t->len] = 0; + t->needsupdate = 4; + return true; + } +} + +// +// HUlib_drawTextLine() +// +// Draws a hu_textline_t widget +// +// Passed the hu_textline_t and flag whether to draw a cursor +// Returns nothing +// +void HUlib_drawTextLine +( hu_textline_t* l, + dboolean drawcursor ) +{ + + int i; + int w; + int x; + unsigned char c; + int oc = l->cm; //jff 2/17/98 remember default color + int y; // killough 1/18/98 -- support multiple lines + + // calculate width of widget + if (l->w == 0) + { + for (i = 0; i < l->len; i++) + { + c = toupper(l->l[i]); + if (c == '\n') + continue; + else if (c == '\x1b') + i++; + else if (c != ' ' && c >= l->sc && c <= 127) + l->w += l->f[c - l->sc].width; + else + l->w += 4; + } + } + + // draw the new stuff + + x = (l->x < 0 ? -l->x - l->w : l->x); + y = (l->y < 0 ? -l->y - l->f[toupper(l->l[0]) - l->sc].height : l->y); + for (i=0;ilen;i++) + { + c = toupper(l->l[i]); //jff insure were not getting a cheap toupper conv. + + if (c=='\n') // killough 1/18/98 -- support multiple lines + x=0,y+=8; + else if (c=='\t') // killough 1/23/98 -- support tab stops + x=x-x%80+80; + else if (c=='\x1b') //jff 2/17/98 escape code for color change + { //jff 3/26/98 changed to actual escape char + if (++ilen) + if (l->l[i]>='0' && l->l[i]<='9') + l->cm = l->l[i]-'0'; + } + else if (c != ' ' && c >= l->sc && c <= 127) + { + w = l->f[c - l->sc].width; + if (x+w-l->f[c - l->sc].leftoffset > BASE_WIDTH) + break; + // killough 1/18/98 -- support multiple lines: + // CPhipps - patch drawing updated + V_DrawNumPatch(x, y, FG, l->f[c - l->sc].lumpnum, l->cm, VPT_TRANS | l->flags); + x += w; + } + else + { + x += 4; + if (x >= BASE_WIDTH) + break; + } + } + l->cm = oc; //jff 2/17/98 restore original color + + // draw the cursor if requested + if (drawcursor && x + l->f['_' - l->sc].width <= BASE_WIDTH) + { + // killough 1/18/98 -- support multiple lines + // CPhipps - patch drawing updated + V_DrawNumPatch(x, y, FG, l->f['_' - l->sc].lumpnum, CR_DEFAULT, VPT_NONE | l->flags); + } +} + +// +// HUlib_eraseTextLine() +// +// Erases a hu_textline_t widget when screen border is behind text +// Sorta called by HU_Erase and just better darn get things straight +// +// Passed the hu_textline_t +// Returns nothing +// +void HUlib_eraseTextLine(hu_textline_t* l) +{ + int y; + + // 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 (!(automapmode & am_active) && viewwindowx && l->needsupdate) + { + int top = l->y; + int bottom = l->y + l->f[0].height - 1; + + top = BETWEEN(0, 200-1, top); + bottom = BETWEEN(0, 200-1, bottom); + + if (l->flags & VPT_STRETCH_MASK) + { + stretch_param_t *params = &stretch_params[l->flags & VPT_ALIGN_MASK]; + top = params->video->y1lookup[top] + params->deltay1; + bottom = params->video->y2lookup[bottom] + params->deltay1; + } + + for (y=top; y<=bottom; y++) + { + if (y < viewwindowy || y >= viewwindowy + viewheight) + R_VideoErase(0, y, SCREENWIDTH); // erase entire line + else + { + // erase left border + R_VideoErase(0, y, viewwindowx); + // erase right border + R_VideoErase(viewwindowx + viewwidth, y, viewwindowx); + } + } + } + + if (l->needsupdate) l->needsupdate--; +} + +//////////////////////////////////////////////////////// +// +// Player message widget (up to 4 lines of text) +// +//////////////////////////////////////////////////////// + +// +// HUlib_initSText() +// +// Initialize a hu_stext_t widget. Set the position, number of lines, font, +// start char of the font, and color range to be used, and whether enabled. +// +// Passed a hu_stext_t, and the values used to initialize +// Returns nothing +// +void HUlib_initSText +( hu_stext_t* s, + int x, + int y, + int h, + const patchnum_t* font, + int startchar, + int cm, //jff 2/16/98 add color range parameter + enum patch_translation_e flags, + dboolean* on ) +{ + + int i; + + s->h = h; + s->on = on; + s->laston = true; + s->cl = 0; + for (i=0;il[i], + x, + y - i*(font[0].height+1), + font, + startchar, + cm, + flags + ); +} + +// +// HUlib_addLineToSText() +// +// Adds a blank line to a hu_stext_t widget +// +// Passed a hu_stext_t +// Returns nothing +// +static void HUlib_addLineToSText(hu_stext_t* s) +{ + + int i; + + // add a clear line + if (++s->cl == s->h) + s->cl = 0; + HUlib_clearTextLine(&s->l[s->cl]); + + // everything needs updating + for (i=0 ; ih ; i++) + s->l[i].needsupdate = 4; + +} + +// +// HUlib_addMessageToSText() +// +// Adds a message line with prefix to a hu_stext_t widget +// +// Passed a hu_stext_t, the prefix string, and a message string +// Returns nothing +// +void HUlib_addMessageToSText(hu_stext_t* s, const char* prefix, const char* msg) +{ + HUlib_addLineToSText(s); + if (prefix) + while (*prefix) + HUlib_addCharToTextLine(&s->l[s->cl], *(prefix++)); + + while (*msg) + HUlib_addCharToTextLine(&s->l[s->cl], *(msg++)); +} + +// +// HUlib_drawSText() +// +// Displays a hu_stext_t widget +// +// Passed a hu_stext_t +// Returns nothing +// +void HUlib_drawSText(hu_stext_t* s) +{ + int i, idx; + hu_textline_t *l; + + if (!*s->on) + return; // if not on, don't draw + + // draw everything + for (i=0 ; ih ; i++) + { + idx = s->cl - i; + if (idx < 0) + idx += s->h; // handle queue of lines + + l = &s->l[idx]; + + // need a decision made here on whether to skip the draw + HUlib_drawTextLine(l, false); // no cursor, please + } +} + +// +// HUlib_eraseSText() +// +// Erases a hu_stext_t widget, when the screen is not fullsize +// +// Passed a hu_stext_t +// Returns nothing +// +void HUlib_eraseSText(hu_stext_t* s) +{ + int i; + + for (i=0 ; ih ; i++) + { + if (s->laston && !*s->on) + s->l[i].needsupdate = 4; + HUlib_eraseTextLine(&s->l[i]); + } + s->laston = *s->on; +} + +//////////////////////////////////////////////////////// +// +// Scrolling message review widget +// +// jff added 2/26/98 +// +//////////////////////////////////////////////////////// + +// +// HUlib_initMText() +// +// Initialize a hu_mtext_t widget. Set the position, width, number of lines, +// font, start char of the font, color range, background font, and whether +// enabled. +// +// Passed a hu_mtext_t, and the values used to initialize +// Returns nothing +// +void HUlib_initMText(hu_mtext_t *m, int x, int y, int w, int h, + const patchnum_t* font, int startchar, int cm, + const patchnum_t* bgfont, enum patch_translation_e flags, dboolean *on) +{ + int i; + + m->nl = 0; + m->nr = 0; + m->cl = -1; //jff 4/28/98 prepare for pre-increment + m->x = x; + m->y = y; + m->w = w; + m->h = h; + m->bg = bgfont; + m->on = on; + for (i=0;il[i], + x, + y + (hud_list_bgon? i+1 : i)*HU_REFRESHSPACING, + font, + startchar, + cm, + flags + ); + } +} + +// +// HUlib_addLineToMText() +// +// Adds a blank line to a hu_mtext_t widget +// +// Passed a hu_mtext_t +// Returns nothing +// +static void HUlib_addLineToMText(hu_mtext_t* m) +{ + // add a clear line + if (++m->cl == hud_msg_lines) + m->cl = 0; + HUlib_clearTextLine(&m->l[m->cl]); + + if (m->nlnl++; + + // needs updating + m->l[m->cl].needsupdate = 4; +} + +// +// HUlib_addMessageToMText() +// +// Adds a message line with prefix to a hu_mtext_t widget +// +// Passed a hu_mtext_t, the prefix string, and a message string +// Returns nothing +// +void HUlib_addMessageToMText(hu_mtext_t* m, const char* prefix, const char* msg) +{ + HUlib_addLineToMText(m); + if (prefix) + while (*prefix) + HUlib_addCharToTextLine(&m->l[m->cl], *(prefix++)); + + while (*msg) + HUlib_addCharToTextLine(&m->l[m->cl], *(msg++)); +} + +// +// HUlib_drawMBg() +// +// Draws a background box which the message display review widget can +// display over +// +// Passed position, width, height, and the background patches +// Returns nothing +// +void HUlib_drawMBg +( int x, + int y, + int w, + int h, + const patchnum_t* bgp +) +{ + int xs = bgp[0].width; + int ys = bgp[0].height; + int i,j; + + // CPhipps - patch drawing updated + // top rows + V_DrawNumPatch(x, y, FG, bgp[0].lumpnum, CR_DEFAULT, VPT_STRETCH); // ul + for (j=x+xs;jon) + return; // if not on, don't draw + + // draw everything + if (hud_list_bgon) + HUlib_drawMBg(m->x,m->y,m->w,m->h,m->bg); + y = m->y + HU_REFRESHSPACING; + for (i=0 ; inl ; i++) + { + idx = m->cl - i; + if (idx < 0) + idx += m->nl; // handle queue of lines + + l = &m->l[idx]; + if (hud_list_bgon) + { + l->x = m->x + 4; + l->y = m->y + (i+1)*HU_REFRESHSPACING; + } + else + { + l->x = m->x; + l->y = m->y + i*HU_REFRESHSPACING; + } + + // need a decision made here on whether to skip the draw + HUlib_drawTextLine(l, false); // no cursor, please + } +} + +// +// HUlib_eraseMBg() +// +// Erases background behind hu_mtext_t widget, when the screen is not fullsize +// +// Passed a hu_mtext_t +// Returns nothing +// +static void HUlib_eraseMBg(hu_mtext_t* m) +{ + int lh; + int y; + + // 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 (!(automapmode & am_active) && viewwindowx) + { + lh = m->l[0].f[0].height + 1; + for (y=m->y; yy+lh*(hud_msg_lines+2) ; y++) + { + if (y < viewwindowy || y >= viewwindowy + viewheight) + R_VideoErase(0, y, SCREENWIDTH); // erase entire line + else + { + // erase left border + R_VideoErase(0, y, viewwindowx); + // erase right border + R_VideoErase(viewwindowx + viewwidth, y, viewwindowx); + + } + } + } +} + +// +// HUlib_eraseMText() +// +// Erases a hu_mtext_t widget, when the screen is not fullsize +// +// Passed a hu_mtext_t +// Returns nothing +// +void HUlib_eraseMText(hu_mtext_t* m) +{ + int i; + + if (hud_list_bgon) + HUlib_eraseMBg(m); + + for (i=0 ; i< m->nl ; i++) + { + m->l[i].needsupdate = 4; + HUlib_eraseTextLine(&m->l[i]); + } +} + +//////////////////////////////////////////////////////// +// +// Interactive text entry widget +// +//////////////////////////////////////////////////////// + +// +// HUlib_initIText() +// +// Initialize a hu_itext_t widget. Set the position, font, +// start char of the font, color range, and whether enabled. +// +// Passed a hu_itext_t, and the values used to initialize +// Returns nothing +// +void HUlib_initIText +( hu_itext_t* it, + int x, + int y, + const patchnum_t* font, + int startchar, + int cm, //jff 2/16/98 add color range parameter + enum patch_translation_e flags, + dboolean* on ) +{ + it->lm = 0; // default left margin is start of text + it->on = on; + it->laston = true; + HUlib_initTextLine(&it->l, x, y, font, startchar, cm, flags); +} + +// The following deletion routines adhere to the left margin restriction + +// +// HUlib_delCharFromIText() +// +// Deletes a character at the end of the text line in a hu_itext_t widget +// +// Passed the hu_itext_t +// Returns nothing +// +static void HUlib_delCharFromIText(hu_itext_t* it) +{ + if (it->l.len != it->lm) + HUlib_delCharFromTextLine(&it->l); +} + +// +// HUlib_eraseLineFromIText() +// +// Deletes all characters from a hu_itext_t widget +// +// Passed the hu_itext_t +// Returns nothing +// +static void HUlib_eraseLineFromIText(hu_itext_t* it) +{ + while (it->lm != it->l.len) + HUlib_delCharFromTextLine(&it->l); +} + +// +// HUlib_resetIText() +// +// Deletes all characters from a hu_itext_t widget +// Resets left margin as well +// +// Passed the hu_itext_t +// Returns nothing +// +void HUlib_resetIText(hu_itext_t* it) +{ + it->lm = 0; + HUlib_clearTextLine(&it->l); +} + +// +// HUlib_addPrefixToIText() +// +// Adds a prefix string passed to a hu_itext_t widget +// Sets left margin to length of string added +// +// Passed the hu_itext_t and the prefix string +// Returns nothing +// +void HUlib_addPrefixToIText +( hu_itext_t* it, + char* str ) +{ + while (*str) + HUlib_addCharToTextLine(&it->l, *(str++)); + it->lm = it->l.len; +} + +// +// HUlib_keyInIText() +// +// Wrapper function for handling general keyed input. +// +// Passed the hu_itext_t and the char input +// Returns true if it ate the key +// +dboolean HUlib_keyInIText +( hu_itext_t* it, + unsigned char ch ) +{ + + if (ch >= ' ' && ch <= '_') + HUlib_addCharToTextLine(&it->l, (char) ch); + else if (ch == key_backspace) // phares + HUlib_delCharFromIText(it); + else if (ch != key_enter) // phares + return false; // did not eat key + + return true; // ate the key +} + +// +// HUlib_drawIText() +// +// Displays a hu_itext_t widget +// +// Passed the hu_itext_t +// Returns nothing +// +void HUlib_drawIText(hu_itext_t* it) +{ + hu_textline_t *l = &it->l; + + if (!*it->on) + return; + HUlib_drawTextLine(l, true); // draw the line w/ cursor +} + +// +// HUlib_eraseIText() +// +// Erases a hu_itext_t widget when the screen is not fullsize +// +// Passed the hu_itext_t +// Returns nothing +// +void HUlib_eraseIText(hu_itext_t* it) +{ + if (it->laston && !*it->on) + it->l.needsupdate = 4; + HUlib_eraseTextLine(&it->l); + it->laston = *it->on; +} + +// +// HUlib_setTextXCenter() +// +// Centering a hu_textline_t +// +// Passed the hu_textline_t +// Returns nothing +// +void HUlib_setTextXCenter(hu_textline_t* t) +{ + char *s = t->l; + t->x = 320; + while (*s) + { + int c = toupper(*(s++)) - HU_FONTSTART; + t->x -= (c < 0 || c > HU_FONTSIZE ? 4 : t->f[c].width); + } + if (t->x < 0) + t->x = 0; + + t->x >>= 1; +} diff --git a/src/hu_lib.h b/src/hu_lib.h new file mode 100644 index 0000000..bd90da3 --- /dev/null +++ b/src/hu_lib.h @@ -0,0 +1,257 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: none + * + *-----------------------------------------------------------------------------*/ + +#ifndef __HULIB__ +#define __HULIB__ + +// We are referring to patches. +#include "r_defs.h" +#include "v_video.h" //jff 2/16/52 include color range defs + + +/* background and foreground screen numbers + * different from other modules. */ +//e6y #define BG 1 +#define FG 0 + +/* font stuff + * #define HU_CHARERASE KEYD_BACKSPACE / not used / phares + */ + +#define HU_MAXLINES 4 +#define HU_MAXLINELENGTH 80 +#define HU_REFRESHSPACING 8 /*jff 2/26/98 space lines in text refresh widget*/ +/*jff 2/26/98 maximum number of messages allowed in refresh list */ +#define HU_MAXMESSAGES 16 + +/* + * 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; + int w; + int val; + + const patchnum_t* f; // font + int sc; // start character + //const char *cr; //jff 2/16/52 output color range + // Proff - Made this an int again. Needed for OpenGL + int cm; //jff 2/16/52 output color range + + // killough 1/23/98: Support multiple lines: + #define MAXLINES 25 + + int linelen; + char l[HU_MAXLINELENGTH*MAXLINES+1]; // line of text + int len; // current line length + + // whether this line needs to be udpated + int needsupdate; + + // e6y: wide-res + enum patch_translation_e flags; +} 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 dboolean stating whether to update window + dboolean* on; + dboolean laston; // last value of *->on. + +} hu_stext_t; + +//jff 2/26/98 new widget to display last hud_msg_lines of messages +// Message refresh window widget +typedef struct +{ + hu_textline_t l[HU_MAXMESSAGES]; // text lines to draw + int nl; // height in lines + int nr; // total height in rows + int cl; // current line number + + int x,y,w,h; // window position and size + const patchnum_t *bg; // patches for background + + // pointer to dboolean stating whether to update window + dboolean* on; + dboolean laston; // last value of *->on. + +} hu_mtext_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 dboolean stating whether to update window + dboolean* on; + dboolean laston; // last value of *->on; + +} hu_itext_t; + + +// +// Widget creation, access, and update routines +// + +// +// textline code +// + +// clear a line of text +void HUlib_clearTextLine(hu_textline_t *t); + +void HUlib_initTextLine +( + hu_textline_t *t, + int x, + int y, + const patchnum_t *f, + int sc, + int cm, //jff 2/16/98 add color range parameter + enum patch_translation_e flags +); + +// returns success +dboolean HUlib_addCharToTextLine(hu_textline_t *t, char ch); + +// draws tline +void HUlib_drawTextLine(hu_textline_t *l, dboolean drawcursor); + +// erases text line +void HUlib_eraseTextLine(hu_textline_t *l); + + +// +// Scrolling Text window widget routines +// + +// initialize an stext widget +void HUlib_initSText +( hu_stext_t* s, + int x, + int y, + int h, + const patchnum_t* font, + int startchar, + int cm, //jff 2/16/98 add color range parameter + enum patch_translation_e flags, + dboolean* on ); + +// add a text message to an stext widget +void HUlib_addMessageToSText(hu_stext_t* s, const char* prefix, const char* msg); + +// draws stext +void HUlib_drawSText(hu_stext_t* s); + +// erases all stext lines +void HUlib_eraseSText(hu_stext_t* s); + +//jff 2/26/98 message refresh widget +// initialize refresh text widget +void HUlib_initMText(hu_mtext_t *m, int x, int y, int w, int h, const patchnum_t* font, + int startchar, int cm, const patchnum_t* bgfont, enum patch_translation_e flags, dboolean *on); + +//jff 2/26/98 message refresh widget +// add a text message to refresh text widget +void HUlib_addMessageToMText(hu_mtext_t* m, const char* prefix, const char* msg); + +//jff 2/26/98 new routine to display a background on which +// the list of last hud_msg_lines are displayed +void HUlib_drawMBg +( int x, + int y, + int w, + int h, + const patchnum_t* bgp +); + +//jff 2/26/98 message refresh widget +// draws mtext +void HUlib_drawMText(hu_mtext_t* m); + +//jff 4/28/98 erases behind message list +void HUlib_eraseMText(hu_mtext_t* m); + +// Input Text Line widget routines +void HUlib_initIText +( hu_itext_t* it, + int x, + int y, + const patchnum_t* font, + int startchar, + int cm, //jff 2/16/98 add color range parameter + enum patch_translation_e flags, + dboolean* on ); + +// resets line and left margin +void HUlib_resetIText(hu_itext_t* it); + +// left of left-margin +void HUlib_addPrefixToIText +( hu_itext_t* it, + char* str ); + +// whether eaten +dboolean HUlib_keyInIText +( hu_itext_t* it, + unsigned char ch ); + +void HUlib_drawIText(hu_itext_t* it); + +// erases all itext lines +void HUlib_eraseIText(hu_itext_t* it); + +//e6y +void HUlib_setTextXCenter(hu_textline_t* t); + +#endif diff --git a/src/hu_stuff.c b/src/hu_stuff.c new file mode 100644 index 0000000..9a0f853 --- /dev/null +++ b/src/hu_stuff.c @@ -0,0 +1,2996 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: Heads-up displays + * + *----------------------------------------------------------------------------- + */ + +// killough 5/3/98: remove unnecessary headers + +#include "doomstat.h" +#include "hu_stuff.h" +#include "hu_lib.h" +#include "hu_tracers.h" +#include "st_stuff.h" /* jff 2/16/98 need loc of status bar */ +#include "s_sound.h" +#include "dstrings.h" +#include "sounds.h" +#include "d_deh.h" /* Ty 03/27/98 - externalization of mapnamesx arrays */ +#include "g_game.h" +#include "r_main.h" +#include "p_inter.h" +#include "p_tick.h" +#include "p_map.h" +#include "sc_man.h" +#include "m_misc.h" +#include "r_main.h" +#include "lprintf.h" +#include "e6y.h" //e6y +#include "g_overflow.h" + +// global heads up display controls + +int hud_displayed; //jff 2/23/98 turns heads-up display on/off +int hud_num; + +// +// Locally used constants, shortcuts. +// +// Ty 03/28/98 - +// These four shortcuts modifed to reflect char ** of mapnamesx[] +// e6y: why sizeof(mapnamest)/sizeof(mapnamest[0]) does not work? +#define HU_TITLE ((gameepisode <= 5 && gamemap <= 9) ? *mapnames[(gameepisode-1)*9+gamemap-1] : s) +#define HU_TITLE2 (gamemap <= 33 ? *mapnames2[gamemap-1] : s) +#define HU_TITLEP (gamemap <= 32 ? *mapnamesp[gamemap-1] : s) +#define HU_TITLET (gamemap <= 32 ? *mapnamest[gamemap-1] : s) +#define HU_TITLEC (gamemap <= 5 ? *mapnames[gamemap-1] : s) +#define HU_TITLEX 0 +//jff 2/16/98 change 167 to ST_Y-1 +// CPhipps - changed to ST_TY +// proff - changed to 200-ST_HEIGHT for stretching +#define HU_TITLEY ((200-ST_HEIGHT) - 1 - hu_font[0].height) + +//jff 2/16/98 add coord text widget coordinates +// proff - changed to SCREENWIDTH to 320 for stretching +#define HU_COORDX (320 - 13*hu_font2['A'-HU_FONTSTART].width) +//jff 3/3/98 split coord widget into three lines in upper right of screen +#define HU_COORDXYZ_Y (1 * hu_font['A'-HU_FONTSTART].height + 1) +#define HU_COORDX_Y (0 + 0*hu_font['A'-HU_FONTSTART].height + HU_COORDXYZ_Y) +#define HU_COORDY_Y (1 + 1*hu_font['A'-HU_FONTSTART].height + HU_COORDXYZ_Y) +#define HU_COORDZ_Y (2 + 2*hu_font['A'-HU_FONTSTART].height + HU_COORDXYZ_Y) + +#define HU_MAP_STAT_X (0) +#define HU_MAP_STAT_Y (1 * hu_font['A'-HU_FONTSTART].height + 1) +#define HU_MAP_MONSTERS_Y (0 + 0*hu_font['A'-HU_FONTSTART].height + HU_MAP_STAT_Y) +#define HU_MAP_SECRETS_Y (1 + 1*hu_font['A'-HU_FONTSTART].height + HU_MAP_STAT_Y) +#define HU_MAP_ITEMS_Y (2 + 2*hu_font['A'-HU_FONTSTART].height + HU_MAP_STAT_Y) +#define HU_MAP_TIME_Y (4 + 4*hu_font['A'-HU_FONTSTART].height + HU_MAP_STAT_Y) +#define HU_MAP_TOTALTIME_Y (5 + 5*hu_font['A'-HU_FONTSTART].height + HU_MAP_STAT_Y) + +//jff 2/16/98 add ammo, health, armor widgets, 2/22/98 less gap +#define HU_GAPY 8 + +#define HU_INPUTX HU_MSGX +#define HU_INPUTY (HU_MSGY + HU_MSGHEIGHT*(hu_font[0].height) +1) + +#define HU_TRACERX (2) +#define HU_TRACERY (hu_font['A'-HU_FONTSTART].height) + +#define key_alt KEYD_RALT +#define key_shift KEYD_RSHIFT + +const char* chat_macros[] = +// Ty 03/27/98 - *not* externalized +// CPhipps - const char* +{ + HUSTR_CHATMACRO0, + HUSTR_CHATMACRO1, + HUSTR_CHATMACRO2, + HUSTR_CHATMACRO3, + HUSTR_CHATMACRO4, + HUSTR_CHATMACRO5, + HUSTR_CHATMACRO6, + HUSTR_CHATMACRO7, + HUSTR_CHATMACRO8, + HUSTR_CHATMACRO9 +}; + +const char* player_names[] = +// Ty 03/27/98 - *not* externalized +// CPhipps - const char* +{ + HUSTR_PLRGREEN, + HUSTR_PLRINDIGO, + HUSTR_PLRBROWN, + HUSTR_PLRRED +}; + +//jff 3/17/98 translate player colmap to text color ranges +int plyrcoltran[MAXPLAYERS]={CR_GREEN,CR_GRAY,CR_BROWN,CR_RED}; + +char chat_char; // remove later. +static player_t* plr; + +// font sets +patchnum_t hu_font[HU_FONTSIZE]; +patchnum_t hu_font2[HU_FONTSIZE]; +patchnum_t hu_fontk[HU_FONTSIZE];//jff 3/7/98 added for graphic key indicators +patchnum_t hu_msgbg[9]; //jff 2/26/98 add patches for message background +patchnum_t hu_font_hud[HU_FONTSIZE]; + +// widgets +static hu_textline_t w_title; +static hu_stext_t w_message; +static hu_itext_t w_chat; +static hu_itext_t w_inputbuffer[MAXPLAYERS]; +static hu_textline_t w_coordx; //jff 2/16/98 new coord widget for automap +static hu_textline_t w_coordy; //jff 3/3/98 split coord widgets automap +static hu_textline_t w_coordz; //jff 3/3/98 split coord widgets automap +static hu_textline_t w_ammo; //jff 2/16/98 new ammo widget for hud +static hu_textline_t w_health; //jff 2/16/98 new health widget for hud +static hu_textline_t w_armor; //jff 2/16/98 new armor widget for hud +static hu_textline_t w_weapon; //jff 2/16/98 new weapon widget for hud +static hu_textline_t w_keys; //jff 2/16/98 new keys widget for hud +static hu_textline_t w_gkeys; //jff 3/7/98 graphic keys widget for hud +static hu_textline_t w_monsec; //jff 2/16/98 new kill/secret widget for hud +static hu_mtext_t w_rtext; //jff 2/26/98 text message refresh widget + +static hu_textline_t w_map_monsters; //e6y monsters widget for automap +static hu_textline_t w_map_secrets; //e6y secrets widgets automap +static hu_textline_t w_map_items; //e6y items widgets automap +static hu_textline_t w_map_time; //e6y level time widgets automap +static hu_textline_t w_map_totaltime; //e6y total time widgets automap + +static hu_textline_t w_health_big; +static hu_textline_t w_medict_icon_big; +static hu_textline_t w_medict_icon_small; +static hu_textline_t w_medict_icon_custom; +static hu_textline_t w_armor_big; +static hu_textline_t w_armor_icon_big; +static hu_textline_t w_armor_icon_small; +static hu_textline_t w_armor_icon_custom; +static hu_textline_t w_medict_percent; +static hu_textline_t w_armor_percent; +static hu_textline_t w_ammo_big; +static hu_textline_t w_ammo_icon; +static hu_textline_t w_keys_icon; + +static dboolean always_off = false; +static char chat_dest[MAXPLAYERS]; +dboolean chat_on; +static dboolean message_on; +static dboolean message_list; //2/26/98 enable showing list of messages +dboolean message_dontfuckwithme; +static dboolean message_nottobefuckedwith; +static int message_counter; +extern int showMessages; +static dboolean headsupactive = false; + +//jff 2/16/98 hud supported automap colors added +int hudcolor_titl; // color range of automap level title +int hudcolor_xyco; // color range of new coords on automap +int hudcolor_mapstat_title; +int hudcolor_mapstat_value; +int hudcolor_mapstat_time; +//jff 2/16/98 hud text colors, controls added +int hudcolor_mesg; // color range of scrolling messages +int hudcolor_chat; // color range of chat lines +int hud_msg_lines; // number of message lines in window +//jff 2/26/98 hud text colors, controls added +int hudcolor_list; // list of messages color +int hud_list_bgon; // enable for solid window background for message list + +//jff 2/16/98 initialization strings for ammo, health, armor widgets +static char hud_coordstrx[32]; +static char hud_coordstry[32]; +static char hud_coordstrz[32]; +static char hud_ammostr[80]; +static char hud_healthstr[80]; +static char hud_armorstr[80]; +static char hud_weapstr[80]; +static char hud_keysstr[80]; +static char hud_gkeysstr[80]; //jff 3/7/98 add support for graphic key display +static char hud_monsecstr[80]; + +// +// Builtin map names. +// The actual names can be found in DStrings.h. +// +// Ty 03/27/98 - externalized map name arrays - now in d_deh.c +// and converted to arrays of pointers to char * +// See modified HUTITLEx macros +extern char **mapnames[]; +extern char **mapnames2[]; +extern char **mapnamesp[]; +extern char **mapnamest[]; + +extern int map_point_coordinates; +extern int map_level_stat; + +// key tables +// jff 5/10/98 french support removed, +// as it was not being used and couldn't be easily tested +// +const char* shiftxform; + +static custom_message_t custom_message[MAXPLAYERS]; +static custom_message_t *custom_message_p; +void HU_init_crosshair(void); + +const char english_shiftxform[] = +{ + 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, + ' ', '!', '"', '#', '$', '%', '&', + '"', // shift-' + '(', ')', '*', '+', + '<', // shift-, + '_', // shift-- + '>', // shift-. + '?', // shift-/ + ')', // shift-0 + '!', // shift-1 + '@', // shift-2 + '#', // shift-3 + '$', // shift-4 + '%', // shift-5 + '^', // shift-6 + '&', // shift-7 + '*', // shift-8 + '(', // shift-9 + ':', + ':', // shift-; + '<', + '+', // shift-= + '>', '?', '@', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + '[', // shift-[ + '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK + ']', // shift-] + '"', '_', + '\'', // shift-` + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + '{', '|', '}', '~', 127 +}; + +static void HU_SetLumpTrans(const char *name) +{ + int lump = W_CheckNumForName(name); + if (lump > 0) + { + lumpinfo[lump].flags |= LUMP_CM2RGB; + } +} + +// +// HU_Init() +// +// Initialize the heads-up display, text that overwrites the primary display +// +// Passed nothing, returns nothing +// +void HU_Init(void) +{ + + int i; + int j; + char buffer[9]; + + shiftxform = english_shiftxform; + + // load the heads-up font + j = HU_FONTSTART; + for (i=0;i122) + { + sprintf(buffer, "STBR%.3d",j); + R_SetPatchNum(&hu_font2[i], buffer); + R_SetPatchNum(&hu_font[i], buffer); + } + else + hu_font[i] = hu_font[0]; //jff 2/16/98 account for gap + } + + // these patches require cm to rgb translation + for (i = 33; i < 96; i++) + { + sprintf(buffer, "STCFN%.3d", i); + HU_SetLumpTrans(buffer); + } + for (i = 0; i < 10; i++) + { + sprintf(buffer, "STTNUM%d", i); + HU_SetLumpTrans(buffer); + } + HU_SetLumpTrans("STTPRCNT"); + HU_SetLumpTrans("STTMINUS"); + + // CPhipps - load patches for message background + for (i=0; i<9; i++) { + sprintf(buffer, "BOX%c%c", "UCL"[i/3], "LCR"[i%3]); + R_SetPatchNum(&hu_msgbg[i], buffer); + } + + // CPhipps - load patches for keys and double keys + for (i=0; i<6; i++) { + sprintf(buffer, "STKEYS%d", i); + R_SetPatchNum(&hu_fontk[i], buffer); + } + + R_SetSpriteByIndex(&hu_font_hud[4], SPR_MEDI); + R_SetSpriteByIndex(&hu_font_hud[5], SPR_ARM1); + R_SetSpriteByIndex(&hu_font_hud[6], SPR_ARM1); + R_SetSpriteByIndex(&hu_font_hud[7], SPR_ARM2); + + R_SetSpriteByIndex(&hu_font_hud[8], SPR_STIM); + R_SetSpriteByName(&hu_font_hud[9], "BON2A0"); + R_SetSpriteByName(&hu_font_hud[10], "BON2B0"); + R_SetSpriteByName(&hu_font_hud[11], "BON2D0"); + + R_SetPatchNum(&hu_font_hud[12], "STTPRCNT"); + R_SetPatchNum(&hu_font_hud[13], "STTPRCNT"); + + R_SetPatchByName(&hu_font_hud[30], "HUDMED"); + R_SetPatchByName(&hu_font_hud[31], "HUDARM1"); + R_SetPatchByName(&hu_font_hud[32], "HUDARM1"); + R_SetPatchByName(&hu_font_hud[33], "HUDARM2"); + + R_SetSpriteByName(&hu_font_hud[40], "CLIPA0"); + R_SetSpriteByName(&hu_font_hud[41], "SHELA0"); + R_SetSpriteByName(&hu_font_hud[42], "CELLA0"); + R_SetSpriteByName(&hu_font_hud[43], "ROCKA0"); + +} + +// +// HU_Stop() +// +// Make the heads-up displays inactive +// +// Passed nothing, returns nothing +// +static void HU_Stop(void) +{ + headsupactive = false; +} + +// +// HU_Start(void) +// +// Create and initialize the heads-up widgets, software machines to +// maintain, update, and display information over the primary display +// +// This routine must be called after any change to the heads up configuration +// in order for the changes to take effect in the actual displays +// +// Passed nothing, returns nothing +// +void HU_Start(void) +{ + int i; + const char* s; /* cph - const */ + + if (headsupactive) // stop before starting + HU_Stop(); + + plr = &players[displayplayer]; // killough 3/7/98 + custom_message_p = &custom_message[displayplayer]; + message_on = false; + message_dontfuckwithme = false; + message_nottobefuckedwith = false; + chat_on = false; + + // create the message widget + // messages to player in upper-left of screen + HUlib_initSText + ( + &w_message, + HU_MSGX, + HU_MSGY, + HU_MSGHEIGHT, + hu_font, + HU_FONTSTART, + hudcolor_mesg, + VPT_ALIGN_LEFT_TOP, + &message_on + ); + + //jff 2/16/98 added some HUD widgets + // create the map title widget - map title display in lower left of automap + HUlib_initTextLine + ( + &w_title, + HU_TITLEX, + HU_TITLEY, + hu_font, + HU_FONTSTART, + hudcolor_titl, + VPT_ALIGN_LEFT_BOTTOM + ); + + // create the hud health widget + // bargraph and number for amount of health, + // lower left or upper right of screen + HUlib_initTextLine + ( + &w_health, + 0, 0, + hu_font2, + HU_FONTSTART, + CR_GREEN, + VPT_NONE + ); + + HUlib_initTextLine + ( + &w_health_big, + 0, 0, + hu_font_hud, + HU_FONTSTART, + CR_RED, + VPT_NONE + ); + + HUlib_initTextLine + ( + &w_medict_icon_big, + 0, 0, + hu_font_hud, + HU_FONTSTART, + CR_RED, + VPT_NONE + ); + + HUlib_initTextLine + ( + &w_medict_icon_small, + 0, 0, + hu_font_hud, + HU_FONTSTART, + CR_RED, + VPT_NONE + ); + + HUlib_initTextLine + ( + &w_medict_icon_custom, + 0, 0, + hu_font_hud, + HU_FONTSTART, + CR_RED, + VPT_NONE + ); + + // create the hud armor widget + // bargraph and number for amount of armor, + // lower left or upper right of screen + HUlib_initTextLine + ( + &w_armor, + 0, 0, + hu_font2, + HU_FONTSTART, + CR_GREEN, + VPT_NONE + ); + + HUlib_initTextLine + ( + &w_armor_big, + 0, 0, + hu_font_hud, + HU_FONTSTART, + CR_RED, + VPT_NONE + ); + + HUlib_initTextLine + ( + &w_armor_icon_big, + 0, 0, + hu_font_hud, + HU_FONTSTART, + CR_RED, + VPT_NONE + ); + + HUlib_initTextLine + ( + &w_armor_icon_small, + 0, 0, + hu_font_hud, + HU_FONTSTART, + CR_RED, + VPT_NONE + ); + + HUlib_initTextLine + ( + &w_armor_icon_custom, + 0, 0, + hu_font_hud, + HU_FONTSTART, + CR_RED, + VPT_NONE + ); + + // create the hud ammo widget + // bargraph and number for amount of ammo for current weapon, + // lower left or lower right of screen + HUlib_initTextLine + ( + &w_ammo, + 0, 0, + hu_font2, + HU_FONTSTART, + CR_GOLD, + VPT_NONE + ); + + // create the hud weapons widget + // list of numbers of weapons possessed + // lower left or lower right of screen + HUlib_initTextLine + ( + &w_weapon, + 0, 0, + hu_font2, + HU_FONTSTART, + CR_GRAY, + VPT_NONE + ); + + // create the hud keys widget + // display of key letters possessed + // lower left of screen + HUlib_initTextLine + ( + &w_keys, + 0, 0, + hu_font2, + HU_FONTSTART, + CR_GRAY, + VPT_NONE + ); + + // create the hud graphic keys widget + // display of key graphics possessed + // lower left of screen + HUlib_initTextLine + ( + &w_gkeys, + 0, 0, + hu_fontk, + HU_FONTSTART, + CR_RED, + VPT_NONE + ); + + // create the hud monster/secret widget + // totals and current values for kills, items, secrets + // lower left of screen + HUlib_initTextLine + ( + &w_monsec, + 0, 0, + hu_font2, + HU_FONTSTART, + CR_GRAY, + VPT_NONE + ); + + HUlib_initTextLine + ( + &w_medict_percent, + 0, 0, + hu_font_hud, + HU_FONTSTART, + CR_GRAY, + VPT_NONE + ); + + HUlib_initTextLine + ( + &w_armor_percent, + 0, 0, + hu_font_hud, + HU_FONTSTART, + CR_GRAY, + VPT_NONE + ); + + HUlib_initTextLine + ( + &w_ammo_big, + 0, 0, + hu_font_hud, + HU_FONTSTART, + CR_RED, + VPT_NONE + ); + + HUlib_initTextLine + ( + &w_ammo_icon, + 0, 0, + hu_font_hud, + HU_FONTSTART, + CR_RED, + VPT_NONE + ); + + HUlib_initTextLine + ( + &w_keys_icon, + 0, 0, + hu_fontk, + HU_FONTSTART, + CR_RED, + VPT_NONE + ); + + // create the hud text refresh widget + // scrolling display of last hud_msg_lines messages received + if (hud_msg_lines>HU_MAXMESSAGES) + hud_msg_lines=HU_MAXMESSAGES; + //jff 4/21/98 if setup has disabled message list while active, turn it off + message_list = hud_msg_lines > 1; //jff 8/8/98 initialize both ways + //jff 2/26/98 add the text refresh widget initialization + HUlib_initMText + ( + &w_rtext, + 0, + 0, + 320, +// SCREENWIDTH, + (hud_msg_lines+2)*HU_REFRESHSPACING, + hu_font, + HU_FONTSTART, + hudcolor_list, + hu_msgbg, + VPT_ALIGN_LEFT_TOP, + &message_list + ); + + if (gamemapinfo && gamemapinfo->levelname) + { + if (gamemapinfo->label) + s = gamemapinfo->label; + else + s = gamemapinfo->mapname; + if (s == gamemapinfo->mapname || strcmp(s, "-") != 0) + { + while (*s) + HUlib_addCharToTextLine(&w_title, *(s++)); + + HUlib_addCharToTextLine(&w_title, ':'); + HUlib_addCharToTextLine(&w_title, ' '); + } + s = gamemapinfo->levelname; + while (*s) + HUlib_addCharToTextLine(&w_title, *(s++)); + + } + else + { + // initialize the automap's level title widget + // e6y: stop SEGV here when gamemap is not initialized + if (gamestate == GS_LEVEL && gamemap > 0) /* cph - stop SEGV here when not in level */ + { + // initialize the map title widget with the generic map lump name + s = MAPNAME(gameepisode, gamemap); + + switch (gamemode) + { + case shareware: + case registered: + case retail: + s = HU_TITLE; + break; + + case commercial: + default: // Ty 08/27/98 - modified to check mission for TNT/Plutonia + s = (gamemission == pack_tnt) ? HU_TITLET : + (gamemission == pack_plut) ? HU_TITLEP : HU_TITLE2; + break; + } + + // Chex.exe always uses the episode 1 level title + // eg. E2M1 gives the title for E1M1 + if (gamemission == chex) + { + s = HU_TITLEC; + } + } + else s = ""; + + while (*s) + HUlib_addCharToTextLine(&w_title, *(s++)); + } + + + // create the automaps coordinate widget + // jff 3/3/98 split coord widget into three lines: x,y,z + // jff 2/16/98 added + HUlib_initTextLine + ( + &w_coordx, + HU_COORDX, + HU_COORDX_Y, + hu_font, + HU_FONTSTART, + hudcolor_xyco, + VPT_ALIGN_RIGHT_TOP + ); + HUlib_initTextLine + ( + &w_coordy, + HU_COORDX, + HU_COORDY_Y, + hu_font, + HU_FONTSTART, + hudcolor_xyco, + VPT_ALIGN_RIGHT_TOP + ); + HUlib_initTextLine + ( + &w_coordz, + HU_COORDX, + HU_COORDZ_Y, + hu_font, + HU_FONTSTART, + hudcolor_xyco, + VPT_ALIGN_RIGHT_TOP + ); +//e6y + HUlib_initTextLine + ( + &w_map_monsters, + HU_MAP_STAT_X, + HU_MAP_MONSTERS_Y, + hu_font, + HU_FONTSTART, + hudcolor_mapstat_title, + VPT_ALIGN_LEFT_TOP + ); + HUlib_initTextLine + ( + &w_map_secrets, + HU_MAP_STAT_X, + HU_MAP_SECRETS_Y, + hu_font, + HU_FONTSTART, + hudcolor_mapstat_title, + VPT_ALIGN_LEFT_TOP + ); + HUlib_initTextLine + ( + &w_map_items, + HU_MAP_STAT_X, + HU_MAP_ITEMS_Y, + hu_font, + HU_FONTSTART, + hudcolor_mapstat_title, + VPT_ALIGN_LEFT_TOP + ); + HUlib_initTextLine + ( + &w_map_time, + HU_MAP_STAT_X, + HU_MAP_TIME_Y, + hu_font, + HU_FONTSTART, + hudcolor_mapstat_time, + VPT_ALIGN_LEFT_TOP + ); + HUlib_initTextLine + ( + &w_map_totaltime, + HU_MAP_STAT_X, + HU_MAP_TOTALTIME_Y, + hu_font, + HU_FONTSTART, + hudcolor_mapstat_time, + VPT_ALIGN_LEFT_TOP + ); + HUlib_initTextLine + ( + &w_hudadd, + 0, 0, + hu_font2, + HU_FONTSTART, + CR_GRAY, + VPT_NONE + ); + HUlib_initTextLine + ( + &w_centermsg, + HU_CENTERMSGX, + HU_CENTERMSGY, + hu_font, + HU_FONTSTART, + hudcolor_titl, + VPT_STRETCH + ); + HUlib_initTextLine + ( + &w_precache, + 16, + 186, + hu_font, + HU_FONTSTART, + CR_RED, + VPT_ALIGN_LEFT_BOTTOM + ); + strcpy(hud_add,""); + s = hud_add; + while (*s) + HUlib_addCharToTextLine(&w_hudadd, *(s++)); + + for(i = 0; i < NUMTRACES; i++) + { + HUlib_initTextLine( + &w_traces[i], + HU_TRACERX, + HU_TRACERY+i*HU_GAPY, + hu_font2, + HU_FONTSTART, + CR_GRAY, + VPT_ALIGN_LEFT_BOTTOM + ); + + strcpy(traces[i].hudstr, ""); + s = traces[i].hudstr; + while (*s) + HUlib_addCharToTextLine(&w_traces[i], *(s++)); + HUlib_drawTextLine(&w_traces[i], false); + } + + + //jff 2/16/98 initialize ammo widget + strcpy(hud_ammostr,"AMM "); + + //jff 2/16/98 initialize health widget + strcpy(hud_healthstr,"HEL "); + + //jff 2/16/98 initialize armor widget + strcpy(hud_armorstr,"ARM "); + + //jff 2/17/98 initialize weapons widget + strcpy(hud_weapstr,"WEA "); + + //jff 2/17/98 initialize keys widget + //jff 3/17/98 show frags in deathmatch mode + strcpy(hud_keysstr,(deathmatch ? "FRG " : "KEY ")); + + //jff 2/17/98 initialize graphic keys widget + strcpy(hud_gkeysstr," "); + + //jff 2/17/98 initialize kills/items/secret widget + strcpy(hud_monsecstr,"STS "); + + // create the chat widget + HUlib_initIText + ( + &w_chat, + HU_INPUTX, + HU_INPUTY, + hu_font, + HU_FONTSTART, + hudcolor_chat, + VPT_NONE, + &chat_on + ); + + // create the inputbuffer widgets, one per player + for (i=0 ; i 0) + { + hud_num = (hud_num + 1) % huds_count; // cycle hud_active + } +} + +typedef void (*HU_widget_build_func)(void); +typedef void (*HU_widget_draw_func)(void); + +typedef struct hud_cfg_item_s +{ + char name[80]; + int x; + int y; +} hud_cfg_item_t; + +typedef struct hud_widget_s +{ + hu_textline_t *hu_textline; + int x; + int y; + enum patch_translation_e flags; + HU_widget_build_func build; + HU_widget_draw_func draw; + const char *name; +} hud_widget_t; + +typedef struct hud_widgets_list_s +{ + int count; + hud_widget_t *items; +} hud_widgets_list_t; + +int huds_count; +hud_widgets_list_t *huds; +hud_widgets_list_t *hud_current; + +void HU_widget_build_ammo(void); +void HU_widget_draw_ammo(void); +void HU_widget_build_weapon(void); +void HU_widget_draw_weapon(void); +void HU_widget_build_keys(void); +void HU_widget_draw_keys(void); +void HU_widget_build_monsec(void); +void HU_widget_draw_monsec(void); +void HU_widget_build_health(void); +void HU_widget_draw_health(void); +void HU_widget_build_armor(void); +void HU_widget_draw_armor(void); +void HU_widget_build_hudadd(void); +void HU_widget_draw_hudadd(void); + +void HU_widget_build_health_big(void); +void HU_widget_draw_health_big(void); +void HU_widget_build_armor_big(void); +void HU_widget_draw_armor_big(void); + +void HU_widget_build_medict_icon_big(void); +void HU_widget_draw_medict_icon_big(void); +void HU_widget_build_armor_icon_big(void); +void HU_widget_draw_armor_icon_big(void); + +void HU_widget_build_medict_icon_small(void); +void HU_widget_draw_medict_icon_small(void); +void HU_widget_build_armor_icon_small(void); +void HU_widget_draw_armor_icon_small(void); + +void HU_widget_build_medict_icon_custom(void); +void HU_widget_draw_medict_icon_custom(void); +void HU_widget_build_armor_icon_custom(void); +void HU_widget_draw_armor_icon_custom(void); + +void HU_widget_build_medict_percent(void); +void HU_widget_draw_medict_percent(void); +void HU_widget_build_armor_percent(void); +void HU_widget_draw_armor_percent(void); + +void HU_widget_build_ammo_big(void); +void HU_widget_draw_ammo_big(void); +void HU_widget_build_ammo_icon(void); +void HU_widget_draw_ammo_icon(void); + +void HU_widget_build_gkeys(void); +void HU_widget_draw_gkeys(void); + +// [FG] draw Time/STS widgets above status bar +static inline dboolean drawTimeSTSwidgets (void) +{ + return hudadd_timests && + viewheight < SCREENHEIGHT && + (!(automapmode & am_active) || + (automapmode & am_overlay)); +} + +static hud_widget_t hud_name_widget[] = +{ + {&w_ammo, 0, 0, 0, HU_widget_build_ammo, HU_widget_draw_ammo, "ammo"}, + {&w_weapon, 0, 0, 0, HU_widget_build_weapon, HU_widget_draw_weapon, "weapon"}, + {&w_keys, 0, 0, 0, HU_widget_build_keys, HU_widget_draw_keys, "keys"}, + {&w_monsec, 0, 0, 0, HU_widget_build_monsec, HU_widget_draw_monsec, "monsec"}, + {&w_health, 0, 0, 0, HU_widget_build_health, HU_widget_draw_health, "health"}, + {&w_armor, 0, 0, 0, HU_widget_build_armor, HU_widget_draw_armor, "armor"}, + {&w_hudadd, 0, 0, 0, HU_widget_build_hudadd, HU_widget_draw_hudadd, "hudadd"}, + + {&w_keys_icon, 0, 0, 0, HU_widget_build_gkeys, HU_widget_draw_gkeys, "gkeys"}, + + {&w_traces[0], 0, 0, 0, NULL, NULL, "tracers"}, + + {&w_health_big, 0, 0, VPT_NOOFFSET, HU_widget_build_health_big, HU_widget_draw_health_big, "health_big"}, + {&w_armor_big, 0, 0, VPT_NOOFFSET, HU_widget_build_armor_big, HU_widget_draw_armor_big, "armor_big"}, + + {&w_medict_icon_big, 0, 0, VPT_NOOFFSET, HU_widget_build_medict_icon_big, HU_widget_draw_medict_icon_big, "medict_icon_big"}, + {&w_armor_icon_big, 0, 0, VPT_NOOFFSET, HU_widget_build_armor_icon_big, HU_widget_draw_armor_icon_big, "armor_icon_big"}, + + {&w_medict_icon_small, 0, 0, VPT_NOOFFSET, HU_widget_build_medict_icon_small, HU_widget_draw_medict_icon_small, "medict_icon_small"}, + {&w_armor_icon_small, 0, 0, VPT_NOOFFSET, HU_widget_build_armor_icon_small, HU_widget_draw_armor_icon_small, "armor_icon_small"}, + + {&w_medict_icon_custom, 0, 0, VPT_NOOFFSET, HU_widget_build_medict_icon_custom, HU_widget_draw_medict_icon_custom, "medict_icon_custom"}, + {&w_armor_icon_custom, 0, 0, VPT_NOOFFSET, HU_widget_build_armor_icon_custom, HU_widget_draw_armor_icon_custom, "armor_icon_custom"}, + + {&w_medict_percent, 0, 0, VPT_NOOFFSET, HU_widget_build_medict_percent, HU_widget_draw_medict_percent, "medict_percent"}, + {&w_armor_percent, 0, 0, VPT_NOOFFSET, HU_widget_build_armor_percent, HU_widget_draw_armor_percent, "armor_percent"}, + + {&w_ammo_big, 0, 0, VPT_NOOFFSET, HU_widget_build_ammo_big, HU_widget_draw_ammo_big, "ammo_big"}, + {&w_ammo_big, 0, 0, VPT_NOOFFSET, HU_widget_build_ammo_big, HU_widget_draw_ammo_big, "ammo_big"}, + {&w_ammo_icon, 0, 0, VPT_NOOFFSET, HU_widget_build_ammo_icon, HU_widget_draw_ammo_icon, "ammo_icon"}, + {&w_ammo_icon, 0, 0, VPT_NOOFFSET, HU_widget_build_ammo_icon, HU_widget_draw_ammo_icon, "ammo_icon"}, + + {NULL, 0, 0, 0, NULL, NULL, NULL} +}; + +void HU_LoadHUDDefs(void) +{ + static int init = 0; + + int lump, i, params_count; + hud_cfg_item_t cfg_item; + hud_widgets_list_t *list = NULL; + char st[200]; + + if (init) + return; + + init = true; + + huds_count = 0; + huds = NULL; + + lump = (W_CheckNumForName)("-PRBHUD-", ns_prboom); + if (lump != -1) + { + SC_OpenLumpByNum(lump); + + // Get actor class name. + while (SC_GetString()) + { + // declaration of hud + if (SC_Compare("hud")) + { + // skip everything after "hud" signature + while (SC_Check()) + SC_GetString(); + + // setup new hud + huds_count++; + huds = realloc(huds, huds_count * sizeof(huds[0])); + list = &huds[huds_count - 1]; + list->items = NULL; + list->count = 0; + + // definition of hud is below + continue; + } + + // keep going until a valid HUD declaration has been found + if (huds_count < 1) + continue; + + strncpy(st, sc_String, sizeof(st) - 1); + + while (SC_Check() && SC_GetString()) + { + strncat(st, " ", sizeof(st) - 1); + strncat(st, sc_String, sizeof(st) - 1); + } + st[sizeof(st) - 1] = 0; + + // hud_widget x y + params_count = sscanf(st, "%s %d %d", &cfg_item.name[0], &cfg_item.x, &cfg_item.y); + if (params_count == 3) + { + for (i = 0; hud_name_widget[i].name; i++) + { + if (!strcasecmp(hud_name_widget[i].name, cfg_item.name)) + { + hud_widget_t *item; + + list->count++; + list->items = realloc(list->items, list->count * sizeof(list->items[0])); + + item = &list->items[list->count - 1]; + + item->hu_textline = hud_name_widget[i].hu_textline; + + item->x = cfg_item.x; + item->y = cfg_item.y; + + if (abs(cfg_item.x) < 160) + { + item->flags = (abs(cfg_item.y) > 100 ? VPT_ALIGN_LEFT_BOTTOM : VPT_ALIGN_LEFT_TOP); + } + else + { + item->flags = (abs(cfg_item.y) > 100 ? VPT_ALIGN_RIGHT_BOTTOM : VPT_ALIGN_RIGHT_TOP); + } + item->flags |= hud_name_widget[i].flags; + + item->build = hud_name_widget[i].build; + item->draw = hud_name_widget[i].draw; + + break; + } + } + } + } + + SC_Close(); + } +} + +// +// HU_MoveHud() +// +// Move the HUD display from distributed to compact mode or vice-versa +// +// Passed nothing, returns nothing +// +//jff 3/9/98 create this externally callable to avoid glitch +// when menu scatter's HUD due to delay in change of position +// + +void HU_MoveHud(int force) +{ + static int ohud_num = -1; + + // [FG] draw Time/STS widgets above status bar + if (viewheight < SCREENHEIGHT) + { + if (force || ohud_num != -2) + { + w_hudadd.x = HU_TITLEX; + w_hudadd.y = HU_TITLEY - HU_GAPY; + w_hudadd.flags = VPT_ALIGN_LEFT_BOTTOM; + + w_monsec.x = HU_TITLEX; + w_monsec.y = HU_TITLEY; + w_monsec.flags = VPT_ALIGN_LEFT_BOTTOM; + + ohud_num = -2; + } + return; + } + + //jff 3/4/98 move displays around on F5 changing hud_distributed + if ((huds_count > 0) && (force || hud_num != ohud_num)) + { + int i; + + hud_current = &huds[hud_num % huds_count]; + + for (i = 0; i < hud_current->count; i++) + { + hud_current->items[i].hu_textline->x = hud_current->items[i].x; + hud_current->items[i].hu_textline->y = hud_current->items[i].y; + hud_current->items[i].hu_textline->flags = hud_current->items[i].flags; + } + + ohud_num = hud_num; + } +} + +int HU_GetHealthColor(int health, int def) +{ + int result; + + if (health < health_red) + result = CR_RED; + else if (health < health_yellow) + result = CR_GOLD; + else if (health <= health_green) + result = CR_GREEN; + else + result = def; + + return result; +} + +int HU_GetArmorColor(int armor, int def) +{ + int result; + + if (sts_armorcolor_type) + { + // armor color dictated by type (Advanced HUD) + if (plr->armortype >= 2) + result = CR_BLUE; + else if (plr->armortype == 1) + result = CR_GREEN; + else + result = CR_RED; + } + else + { + // armor color dictated by percent only, not type (Advanced HUD) + if (armor < armor_red) + result = CR_RED; + else if (armor < armor_yellow) + result = CR_GOLD; + else if (armor <= armor_green) + result = CR_GREEN; + else + result = def; + } + return result; +} + +int HU_GetAmmoColor(int ammo, int fullammo, int def, int tofire, dboolean backpack) +{ + int result, ammopct; + + if (ammo < tofire) + result = CR_BROWN; + else if ((ammo==fullammo) || + (ammo_colour_behaviour == ammo_colour_behaviour_no && backpack && ammo*2 >= fullammo)) + result=def; + else { + ammopct = (100 * ammo) / fullammo; + if (backpack && ammo_colour_behaviour != ammo_colour_behaviour_yes) + ammopct *= 2; + if (ammopct < ammo_red) + result = CR_RED; + else if (ammopct < ammo_yellow) + result = CR_GOLD; + else + result = CR_GREEN; + } + + return result; +} + +void HU_widget_build_ammo(void) +{ + int i; + char *s; + char ammostr[80]; //jff 3/8/98 allow plenty room for dehacked mods + int fullammo = plr->maxammo[weaponinfo[plr->readyweapon].ammo]; + + // do the hud ammo display + // clear the widgets internal line + HUlib_clearTextLine(&w_ammo); + strcpy(hud_ammostr,"AMM "); + if (weaponinfo[plr->readyweapon].ammo == am_noammo || fullammo == 0) + { // special case for weapon with no ammo selected - blank bargraph + N/A + strcat(hud_ammostr,"\x7f\x7f\x7f\x7f\x7f\x7f\x7f N/A"); + w_ammo.cm = CR_GRAY; + } + else + { + int ammo = plr->ammo[weaponinfo[plr->readyweapon].ammo]; + int ammopct = (100*ammo)/fullammo; + int ammobars = ammopct/4; + + // build the numeric amount init string + sprintf(ammostr,"%d/%d",ammo,fullammo); + // build the bargraph string + // full bargraph chars + for (i=4;i<4+ammobars/4;) + hud_ammostr[i++] = 123; + // plus one last character with 0,1,2,3 bars + switch(ammobars%4) + { + case 0: + break; + case 1: + hud_ammostr[i++] = 126; + break; + case 2: + hud_ammostr[i++] = 125; + break; + case 3: + hud_ammostr[i++] = 124; + break; + } + + // pad string with blank bar characters + while(i<4+7) + hud_ammostr[i++] = 127; + hud_ammostr[i] = '\0'; + strcat(hud_ammostr,ammostr); + + // set the display color from the percentage of total ammo held + w_ammo.cm = HU_GetAmmoColor(ammo, fullammo, CR_BLUE, + ammopershot[plr->readyweapon], plr->backpack); + } + // transfer the init string to the widget + s = hud_ammostr; + while (*s) + HUlib_addCharToTextLine(&w_ammo, *(s++)); +} + +void HU_widget_draw_ammo(void) +{ + // display the ammo widget every frame + HUlib_drawTextLine(&w_ammo, false); +} + +void HU_widget_build_health(void) +{ + int i; + char *s; + char healthstr[80];//jff + int health = plr->health; + int healthbars = health>100? 25 : health/4; + + if (w_health.val != -1 && w_health.val == health) + return; + w_health.val = health; + + // clear the widgets internal line + HUlib_clearTextLine(&w_health); + + // build the numeric amount init string + sprintf(healthstr,"%3d",health); + // build the bargraph string + // full bargraph chars + for (i=4;i<4+healthbars/4;) + hud_healthstr[i++] = 123; + // plus one last character with 0,1,2,3 bars + switch(healthbars%4) + { + case 0: + break; + case 1: + hud_healthstr[i++] = 126; + break; + case 2: + hud_healthstr[i++] = 125; + break; + case 3: + hud_healthstr[i++] = 124; + break; + } + // pad string with blank bar characters + while(i<4+7) + hud_healthstr[i++] = 127; + hud_healthstr[i] = '\0'; + strcat(hud_healthstr,healthstr); + + // set the display color from the amount of health posessed + w_health.cm = HU_GetHealthColor(health, CR_BLUE); + + // transfer the init string to the widget + s = hud_healthstr; + while (*s) + HUlib_addCharToTextLine(&w_health, *(s++)); +} + +void HU_widget_draw_health(void) +{ + HUlib_drawTextLine(&w_health, false); +} + +void HU_widget_build_health_big(void) +{ + char *s; + char healthstr[80];//jff + int health = plr->health; + + if (w_health_big.val != -1 && w_health_big.val == health) + return; + w_health_big.val = health; + + // clear the widgets internal line + HUlib_clearTextLine(&w_health_big); + + // build the numeric amount init string + sprintf(healthstr,"%d",health); + + // set the display color from the amount of health posessed + if (!sts_always_red) + w_health_big.cm = HU_GetHealthColor(health, CR_BLUE2); + + // transfer the init string to the widget + s = healthstr; + while (*s) + HUlib_addCharToTextLine(&w_health_big, *(s++)); +} + +void HU_widget_draw_health_big(void) +{ + HUlib_drawTextLine(&w_health_big, false); +} + +void HU_widget_build_medict_icon_big(void) +{ + // transfer the graphic key text to the widget + HUlib_clearTextLine(&w_medict_icon_big); + HUlib_addCharToTextLine(&w_medict_icon_big, '!' + 0 + 4); +} + +void HU_widget_draw_medict_icon_big(void) +{ + HUlib_drawTextLine(&w_medict_icon_big, false); +} + +void HU_widget_build_medict_icon_small(void) +{ + // transfer the graphic key text to the widget + HUlib_clearTextLine(&w_medict_icon_small); + HUlib_addCharToTextLine(&w_medict_icon_small, '!' + 0 + 8); +} + +void HU_widget_draw_medict_icon_small(void) +{ + HUlib_drawTextLine(&w_medict_icon_small, false); +} + +void HU_widget_build_medict_icon_custom(void) +{ + // transfer the graphic key text to the widget + HUlib_clearTextLine(&w_medict_icon_custom); + HUlib_addCharToTextLine(&w_medict_icon_custom, '!' + 0 + 30); +} + +void HU_widget_draw_medict_icon_custom(void) +{ + HUlib_drawTextLine(&w_medict_icon_custom, false); +} + +void HU_widget_build_armor_icon_custom(void) +{ + // transfer the graphic key text to the widget + HUlib_clearTextLine(&w_armor_icon_custom); + HUlib_addCharToTextLine(&w_armor_icon_custom, (char)('!' + plr->armortype + 1 + 30)); +} + +void HU_widget_draw_armor_icon_custom(void) +{ + HUlib_drawTextLine(&w_armor_icon_custom, false); +} + +void HU_widget_build_armor(void) +{ + int i; + char *s; + char armorstr[80]; //jff + int armor = plr->armorpoints; + int armorbars = armor>100? 25 : armor/4; + + if (w_armor.val != -1 && w_armor.val == armor) + return; + w_armor.val = armor; + + // clear the widgets internal line + HUlib_clearTextLine(&w_armor); + // build the numeric amount init string + sprintf(armorstr,"%3d",armor); + // build the bargraph string + // full bargraph chars + for (i=4;i<4+armorbars/4;) + hud_armorstr[i++] = 123; + // plus one last character with 0,1,2,3 bars + switch(armorbars%4) + { + case 0: + break; + case 1: + hud_armorstr[i++] = 126; + break; + case 2: + hud_armorstr[i++] = 125; + break; + case 3: + hud_armorstr[i++] = 124; + break; + } + // pad string with blank bar characters + while(i<4+7) + hud_armorstr[i++] = 127; + hud_armorstr[i] = '\0'; + strcat(hud_armorstr,armorstr); + + // set the display color from the amount of armor posessed + w_armor.cm = HU_GetArmorColor(armor, CR_BLUE); + + // transfer the init string to the widget + s = hud_armorstr; + while (*s) + HUlib_addCharToTextLine(&w_armor, *(s++)); +} + +void HU_widget_draw_armor(void) +{ + HUlib_drawTextLine(&w_armor, false); +} + +void HU_widget_build_armor_big(void) +{ + char *s; + char armorstr[80]; //jff + int armor = plr->armorpoints; + + if (w_armor_big.val != -1 && w_armor_big.val == armor) + return; + w_armor_big.val = armor; + + // clear the widgets internal line + HUlib_clearTextLine(&w_armor_big); + // build the numeric amount init string + sprintf(armorstr,"%d",armor); + + // set the display color from the amount of armor posessed + if (!sts_always_red) + w_armor_big.cm = HU_GetArmorColor(armor, CR_BLUE2); + + // transfer the init string to the widget + s = armorstr; + while (*s) + HUlib_addCharToTextLine(&w_armor_big, *(s++)); +} + +void HU_widget_draw_armor_big(void) +{ + HUlib_drawTextLine(&w_armor_big, false); +} + +void HU_widget_build_armor_icon_big(void) +{ + // transfer the graphic key text to the widget + HUlib_clearTextLine(&w_armor_icon_big); + HUlib_addCharToTextLine(&w_armor_icon_big, (char)('!' + plr->armortype + 1 + 4)); +} + +void HU_widget_draw_armor_icon_big(void) +{ + HUlib_drawTextLine(&w_armor_icon_big, false); +} + +void HU_widget_build_armor_icon_small(void) +{ + // transfer the graphic key text to the widget + HUlib_clearTextLine(&w_armor_icon_small); + HUlib_addCharToTextLine(&w_armor_icon_small, (char)('!' + plr->armortype + 1 + 8)); +} + +void HU_widget_draw_armor_icon_small(void) +{ + HUlib_drawTextLine(&w_armor_icon_small, false); +} + + +void HU_widget_build_weapon(void) +{ + int i; + char *s; + int w; + int ammo,fullammo,ammopct; + + // clear the widgets internal line + HUlib_clearTextLine(&w_weapon); + i=4; hud_weapstr[i] = '\0'; //jff 3/7/98 make sure ammo goes away + + // do each weapon that exists in current gamemode + for (w=0;w<=wp_supershotgun;w++) //jff 3/4/98 show fists too, why not? + { + int ok=1; + //jff avoid executing for weapons that do not exist + switch (gamemode) + { + case shareware: + if (w>=wp_plasma && w!=wp_chainsaw) + ok=0; + break; + case retail: + case registered: + if (w>=wp_supershotgun) + ok=0; + break; + default: + case commercial: + break; + } + if (!ok) continue; + + ammo = plr->ammo[weaponinfo[w].ammo]; + fullammo = plr->maxammo[weaponinfo[w].ammo]; + ammopct=0; + + // skip weapons not currently posessed + if (!plr->weaponowned[w]) + continue; + + // display each weapon number in a color related to the ammo for it + hud_weapstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + if (weaponinfo[w].ammo==am_noammo) //jff 3/14/98 show berserk on HUD + hud_weapstr[i++] = plr->powers[pw_strength]? '0'+CR_GREEN : '0'+CR_GRAY; + else if (ammobackpack && ammo*2 >= fullammo))) + hud_weapstr[i++] = '0'+CR_BLUE; + else + { + ammopct = fullammo ? (100*ammo)/fullammo : 100; + if (plr->backpack && fullammo && + ammo_colour_behaviour != ammo_colour_behaviour_yes) + ammopct *= 2; + if (ammopctcards[k]) + continue; + + hud_gkeysstr[i++] = '!'+k; // key number plus '!' is char for key + hud_gkeysstr[i++] = ' '; // spacing + hud_gkeysstr[i++] = ' '; + } + hud_gkeysstr[i]='\0'; + } + else // not possible in current code, unless deathmatching, + { + i=4; + hud_keysstr[i] = '\0'; //jff 3/7/98 make sure deleted keys go away + + // if deathmatch, build string showing top four frag counts + if (deathmatch) //jff 3/17/98 show frags, not keys, in deathmatch + { + int top1=-999,top2=-999,top3=-999,top4=-999; + int idx1=-1,idx2=-1,idx3=-1,idx4=-1; + int fragcount,m; + char numbuf[32]; + + // scan thru players + for (k=0;ktop1) + { + top4=top3; top3=top2; top2 = top1; top1=fragcount; + idx4=idx3; idx3=idx2; idx2 = idx1; idx1=k; + } + else if (fragcount>top2) + { + top4=top3; top3=top2; top2=fragcount; + idx4=idx3; idx3=idx2; idx2=k; + } + else if (fragcount>top3) + { + top4=top3; top3=fragcount; + idx4=idx3; idx3=k; + } + else if (fragcount>top4) + { + top4=fragcount; + idx4=k; + } + } + // if the biggest number exists, put it in the init string + if (idx1>-1) + { + sprintf(numbuf,"%5d",top1); + // make frag count in player's color via escape code + hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + hud_keysstr[i++] = '0'+plyrcoltran[idx1&3]; + s = numbuf; + while (*s) + hud_keysstr[i++] = *(s++); + } + // if the second biggest number exists, put it in the init string + if (idx2>-1) + { + sprintf(numbuf,"%5d",top2); + // make frag count in player's color via escape code + hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + hud_keysstr[i++] = '0'+plyrcoltran[idx2&3]; + s = numbuf; + while (*s) + hud_keysstr[i++] = *(s++); + } + // if the third biggest number exists, put it in the init string + if (idx3>-1) + { + sprintf(numbuf,"%5d",top3); + // make frag count in player's color via escape code + hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + hud_keysstr[i++] = '0'+plyrcoltran[idx3&3]; + s = numbuf; + while (*s) + hud_keysstr[i++] = *(s++); + } + // if the fourth biggest number exists, put it in the init string + if (idx4>-1) + { + sprintf(numbuf,"%5d",top4); + // make frag count in player's color via escape code + hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + hud_keysstr[i++] = '0'+plyrcoltran[idx4&3]; + s = numbuf; + while (*s) + hud_keysstr[i++] = *(s++); + } + hud_keysstr[i] = '\0'; + } //jff 3/17/98 end of deathmatch clause + else // build alphabetical key display (not used currently) + { + // scan the keys + for (k=0;k<6;k++) + { + // skip any not possessed by the displayed player's stats + if (!plr->cards[k]) + continue; + + // use color escapes to make text in key's color + hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + switch(k) + { + case 0: + hud_keysstr[i++] = '0'+CR_BLUE; + hud_keysstr[i++] = 'B'; + hud_keysstr[i++] = 'C'; + hud_keysstr[i++] = ' '; + break; + case 1: + hud_keysstr[i++] = '0'+CR_GOLD; + hud_keysstr[i++] = 'Y'; + hud_keysstr[i++] = 'C'; + hud_keysstr[i++] = ' '; + break; + case 2: + hud_keysstr[i++] = '0'+CR_RED; + hud_keysstr[i++] = 'R'; + hud_keysstr[i++] = 'C'; + hud_keysstr[i++] = ' '; + break; + case 3: + hud_keysstr[i++] = '0'+CR_BLUE; + hud_keysstr[i++] = 'B'; + hud_keysstr[i++] = 'S'; + hud_keysstr[i++] = ' '; + break; + case 4: + hud_keysstr[i++] = '0'+CR_GOLD; + hud_keysstr[i++] = 'Y'; + hud_keysstr[i++] = 'S'; + hud_keysstr[i++] = ' '; + break; + case 5: + hud_keysstr[i++] = '0'+CR_RED; + hud_keysstr[i++] = 'R'; + hud_keysstr[i++] = 'S'; + hud_keysstr[i++] = ' '; + break; + } + hud_keysstr[i]='\0'; + } + } + } + + HUlib_clearTextLine(&w_keys); // clear the widget strings + + // transfer the built string (frags or key title) to the widget + s = hud_keysstr; //jff 3/7/98 display key titles/key text or frags + while (*s) + HUlib_addCharToTextLine(&w_keys, *(s++)); + + //jff 3/17/98 show graphic keys in non-DM only + if (!deathmatch) //jff 3/7/98 display graphic keys + { + // clear the widget strings + HUlib_clearTextLine(&w_gkeys); + // transfer the graphic key text to the widget + s = hud_gkeysstr; + while (*s) + HUlib_addCharToTextLine(&w_gkeys, *(s++)); + } + + w_gkeys.x = w_keys.x + 20; + w_gkeys.y = w_keys.y; + w_gkeys.flags = w_keys.flags; +} + +void HU_widget_draw_keys(void) +{ + HUlib_drawTextLine(&w_keys, false); + if (!deathmatch) + { + HUlib_drawTextLine(&w_gkeys, false); + } +} + +void HU_widget_build_monsec(void) +{ + int i; + char *s; + + //e6y + char allkills[200], allsecrets[200]; + int playerscount; + int fullkillcount, fullitemcount, fullsecretcount; + int color, killcolor, itemcolor, secretcolor; + + // clear the internal widget text buffer + HUlib_clearTextLine(&w_monsec); + if (!hudadd_smarttotals || deathmatch) + { + sprintf + ( + hud_monsecstr, + "STS \x1b\x36K \x1b\x33%d \x1b\x36M \x1b\x33%d \x1b\x37I \x1b\x33%d/%d \x1b\x35S \x1b\x33%d/%d", + plr->killcount,totallive, + plr->itemcount,totalitems, + plr->secretcount,totalsecret + ); + } + else + { + int allkills_len = 0; + int allsecrets_len = 0; + + playerscount = 0; + fullkillcount = 0; + fullitemcount = 0; + fullsecretcount = 0; + for (i=0 ; i= 0 && allsecrets_len >=0) + { + allkills_len += sprintf(&allkills[allkills_len], "\x1b%c+%d", color, players[i].killcount - players[i].resurectedkillcount); + allsecrets_len += sprintf(&allsecrets[allsecrets_len], "\x1b%c+%d", color, players[i].secretcount); + } + } + playerscount++; + fullkillcount += players[i].killcount - players[i].resurectedkillcount; + fullitemcount += players[i].itemcount; + fullsecretcount += players[i].secretcount; + } + } + killcolor = (fullkillcount >= totalkills ? 0x37 : 0x35); + secretcolor = (fullsecretcount >= totalsecret ? 0x37 : 0x35); + itemcolor = (fullitemcount >= totalitems ? 0x37 : 0x35); + if (playerscount<2) + { + sprintf + ( + hud_monsecstr, + "STS \x1b\x36K \x1b%c%d/%d \x1b\x36I \x1b%c%d/%d \x1b\x36S \x1b%c%d/%d", + killcolor, fullkillcount,totalkills, + itemcolor,plr->itemcount,totalitems, + secretcolor, fullsecretcount,totalsecret + ); + } + else + { + sprintf + ( + hud_monsecstr, + "STS \x1b\x36K %s \x1b%c%d/%d \x1b\x36I \x1b%c%d/%d \x1b\x36S %s \x1b%c%d/%d", + allkills,killcolor,fullkillcount,totalkills, + itemcolor,plr->itemcount,totalitems, + allsecrets,secretcolor,fullsecretcount,totalsecret + ); + } + } + + // transfer the init string to the widget + s = hud_monsecstr; + while (*s) + HUlib_addCharToTextLine(&w_monsec, *(s++)); +} + +void HU_widget_draw_monsec(void) +{ + HUlib_drawTextLine(&w_monsec, false); +} + +void HU_widget_build_hudadd(void) +{ + char *s; + hud_add[0] = 0; + + if (!hudadd_gamespeed && !hudadd_leveltime) + return; + + if (hudadd_gamespeed) + sprintf(hud_add,"\x1b\x32speed \x1b\x33%.2d ", realtic_clock_rate); + if ((hudadd_leveltime) || (demoplayback && hudadd_demotime)) + { + static char demo_len_null[1]={0}; + char *demo_len = demoplayback && hudadd_demotime ? demo_len_st : demo_len_null; + if (totalleveltimes) + sprintf(hud_add+strlen(hud_add),"\x1b\x32time \x1b\x35%d:%02d%s \x1b\x33%d:%05.2f ", + (totalleveltimes+leveltime)/35/60, ((totalleveltimes+leveltime)%(60*35))/35, demo_len, + leveltime/35/60, (float)(leveltime%(60*35))/35); + else + sprintf(hud_add+strlen(hud_add),"\x1b\x32time \x1b\x33%d:%05.2f%s ", + leveltime/35/60, (float)(leveltime%(60*35))/35, demo_len); + } + HUlib_clearTextLine(&w_hudadd); + s = hud_add; + while (*s) + HUlib_addCharToTextLine(&w_hudadd, *(s++)); +} + +void HU_widget_draw_hudadd(void) +{ + if (hudadd_gamespeed || hudadd_leveltime) + { + HUlib_drawTextLine(&w_hudadd, false); + } +} + +void HU_widget_build_medict_percent(void) +{ + int health = plr->health; + + if (w_medict_percent.val != -1 && w_medict_percent.val == health) + return; + w_medict_percent.val = health; + + // transfer the graphic key text to the widget + HUlib_clearTextLine(&w_medict_percent); + + if (!sts_always_red) + { + if (sts_pct_always_gray) + w_medict_percent.cm = CR_GRAY; + else + w_medict_percent.cm = HU_GetHealthColor(health, CR_BLUE2); + } + + HUlib_addCharToTextLine(&w_medict_percent, (char)('!' + 12)); +} + +void HU_widget_draw_medict_percent(void) +{ + HUlib_drawTextLine(&w_medict_percent, false); +} + +void HU_widget_build_armor_percent(void) +{ + int armor = plr->armorpoints; + + if (w_armor_percent.val != -1 && w_armor_percent.val == armor) + return; + w_armor_percent.val = armor; + + // transfer the graphic key text to the widget + HUlib_clearTextLine(&w_armor_percent); + + if (!sts_always_red) + { + if (sts_pct_always_gray) + w_armor_percent.cm = CR_GRAY; + else + w_armor_percent.cm = HU_GetArmorColor(armor, CR_BLUE2); + } + + HUlib_addCharToTextLine(&w_armor_percent, (char)('!' + 13)); +} + +void HU_widget_draw_armor_percent(void) +{ + HUlib_drawTextLine(&w_armor_percent, false); +} + +void HU_widget_build_ammo_big(void) +{ + char *s; + char ammostr[80]; + int fullammo = plr->maxammo[weaponinfo[plr->readyweapon].ammo]; + + // clear the widgets internal line + HUlib_clearTextLine(&w_ammo_big); + + if (weaponinfo[plr->readyweapon].ammo != am_noammo && fullammo != 0) + { + int ammo = plr->ammo[weaponinfo[plr->readyweapon].ammo]; + + // build the numeric amount init string + sprintf(ammostr, "%d", ammo); + + // set the display color from the percentage of total ammo held + if (!sts_always_red) + w_ammo_big.cm = HU_GetAmmoColor(ammo, fullammo, CR_BLUE2, + ammopershot[plr->readyweapon], plr->backpack); + + // transfer the init string to the widget + s = ammostr; + while (*s) + HUlib_addCharToTextLine(&w_ammo_big, *(s++)); + } +} + +void HU_widget_draw_ammo_big(void) +{ + HUlib_drawTextLine(&w_ammo_big, false); +} + +void HU_widget_build_ammo_icon(void) +{ + int ammo = weaponinfo[plr->readyweapon].ammo; + + if (w_ammo_icon.val != -1 && w_ammo_icon.val == ammo) + return; + w_ammo_icon.val = ammo; + + if (ammo < NUMAMMO) + { + HUlib_clearTextLine(&w_ammo_icon); + HUlib_addCharToTextLine(&w_ammo_icon, (char)('!' + ammo + 40)); + } + else + { + HUlib_clearTextLine(&w_ammo_icon); + } +} + +void HU_widget_draw_ammo_icon(void) +{ + HUlib_drawTextLine(&w_ammo_icon, false); +} + +void HU_widget_build_gkeys(void) +{ + int i, k; + char *s; + char gkeysstr[80]; + int mask = 0; + + // build text string whose characters call out graphic keys from fontk + i = 0; + for (k = 0; k < 6; k++) + { + // skip keys not possessed + if (!plr->cards[k]) + continue; + + gkeysstr[i++] = '!' + k; // key number plus '!' is char for key + gkeysstr[i++] = ' '; // spacing + gkeysstr[i++] = ' '; + + mask |= (1 << k); + } + + if (w_keys_icon.val != -1 && w_keys_icon.val == mask) + return; + w_keys_icon.val = mask; + + gkeysstr[i] = '\0'; + while (((--i) > 0) && (gkeysstr[i] == ' ')) + gkeysstr[i] = '\0'; + + // clear the widget strings + HUlib_clearTextLine(&w_keys_icon); + // transfer the graphic key text to the widget + s = gkeysstr; + while (*s) + HUlib_addCharToTextLine(&w_keys_icon, *(s++)); + +} + +void HU_widget_draw_gkeys(void) +{ + HUlib_drawTextLine(&w_keys_icon, false); +} + +const char *crosshair_nam[HU_CROSSHAIRS]= { NULL, "CROSS1", "CROSS2", "CROSS3" }; +const char *crosshair_str[HU_CROSSHAIRS]= { "none", "cross", "angle", "dot" }; +crosshair_t crosshair; + +void HU_init_crosshair(void) +{ + if (!hudadd_crosshair || !crosshair_nam[hudadd_crosshair]) + return; + + crosshair.lump = W_CheckNumForNameInternal(crosshair_nam[hudadd_crosshair]); + if (crosshair.lump == -1) + return; + + crosshair.w = R_NumPatchWidth(crosshair.lump); + crosshair.h = R_NumPatchHeight(crosshair.lump); + + crosshair.flags = VPT_TRANS; + if (hudadd_crosshair_scale) + crosshair.flags |= VPT_STRETCH; +} + +void SetCrosshairTarget(void) +{ + crosshair.target_screen_x = 0.0f; + crosshair.target_screen_y = 0.0f; + + if (hudadd_crosshair_lock_target && crosshair.target_sprite >= 0) + { + float x, y, z; + float winx, winy, winz; + + x = -(float)crosshair.target_x / MAP_SCALE; + z = (float)crosshair.target_y / MAP_SCALE; + y = (float)crosshair.target_z / MAP_SCALE; + + if (R_Project(x, y, z, &winx, &winy, &winz)) + { + int top, bottom, h; + stretch_param_t *params = &stretch_params[crosshair.flags & VPT_ALIGN_MASK]; + + if (V_GetMode() != VID_MODEGL) + { + winy += (float)(viewheight/2 - centery); + } + + top = SCREENHEIGHT - viewwindowy; + h = crosshair.h; + if (hudadd_crosshair_scale) + { + h = h * params->video->height / 200; + } + bottom = top - viewheight + h; + winy = BETWEEN(bottom, top, winy); + + if (!hudadd_crosshair_scale) + { + crosshair.target_screen_x = winx - (crosshair.w / 2); + crosshair.target_screen_y = SCREENHEIGHT - winy - (crosshair.h / 2); + } + else + { + crosshair.target_screen_x = (winx - params->deltax1) * 320.0f / params->video->width - (crosshair.w / 2); + crosshair.target_screen_y = 200 - (winy - params->deltay1) * 200.0f / params->video->height - (crosshair.h / 2); + } + } + } +} + +void HU_draw_crosshair(void) +{ + int cm; + + crosshair.target_sprite = -1; + + if (!crosshair_nam[hudadd_crosshair] || crosshair.lump == -1 || automapmode & am_active || + menuactive != mnact_inactive || paused) + { + return; + } + + if (hudadd_crosshair_health) + cm = HU_GetHealthColor(plr->health, CR_BLUE2); + else + cm = hudadd_crosshair_color; + + if (hudadd_crosshair_target || hudadd_crosshair_lock_target) + { + fixed_t range, slope; + angle_t an = plr->mo->angle; + ammotype_t ammo = weaponinfo[plr->readyweapon].ammo; + + // intercepts overflow guard + overflows_enabled = false; + range = (ammo == am_noammo) ? MELEERANGE : 16*64*FRACUNIT; + slope = P_AimLineAttack(plr->mo, an, range, 0); + if (ammo == am_misl || ammo == am_cell) + { + if (!linetarget) + slope = P_AimLineAttack(plr->mo, an += 1<<26, range, 0); + if (!linetarget) + slope = P_AimLineAttack(plr->mo, an -= 2<<26, range, 0); + } + overflows_enabled = true; + + if (linetarget && !(linetarget->flags & MF_SHADOW)) + { + crosshair.target_x = linetarget->x; + crosshair.target_y = linetarget->y; + crosshair.target_z = linetarget->z; + crosshair.target_z += linetarget->height / 2 + linetarget->height / 8; + crosshair.target_sprite = linetarget->sprite; + + if (hudadd_crosshair_target) + cm = hudadd_crosshair_target_color; + } + } + + SetCrosshairTarget(); + + if (crosshair.target_screen_x != 0) + { + float x = crosshair.target_screen_x; + float y = crosshair.target_screen_y; + V_DrawNumPatchPrecise(x, y, 0, crosshair.lump, cm, crosshair.flags); + } + else + { + int x, y, st_height; + + if (!hudadd_crosshair_scale) + { + st_height = (viewheight != SCREENHEIGHT ? ST_SCALED_HEIGHT : 0); + x = (SCREENWIDTH - crosshair.w) / 2; + y = (SCREENHEIGHT - st_height - crosshair.h) / 2; + } + else + { + st_height = (viewheight != SCREENHEIGHT ? ST_HEIGHT : 0); + x = (320 - crosshair.w) / 2; + y = (200 - st_height - crosshair.h) / 2; + } + + V_DrawNumPatch(x, y, 0, crosshair.lump, cm, crosshair.flags); + } +} + +// +// HU_Drawer() +// +// Draw all the pieces of the heads-up display +// +// Passed nothing, returns nothing +// +void HU_Drawer(void) +{ + char *s; + player_t *plr; + //jff 3/4/98 speed update up for slow systems + //e6y: speed update for uncapped framerate + static dboolean needupdate = false; + if (realframe) needupdate = !needupdate; + + // don't draw anything if there's a fullscreen menu up + if (menuactive == mnact_full) + return; + + plr = &players[displayplayer]; // killough 3/7/98 + // draw the automap widgets if automap is displayed + if (automapmode & am_active) + { + if ((!(automapmode & am_overlay) || (viewheight != SCREENHEIGHT)) && !drawTimeSTSwidgets()) + { + // map title + HUlib_drawTextLine(&w_title, false); + } + + //jff 2/16/98 output new coord display + // x-coord + if (map_point_coordinates) + { + + //e6y: speedup + if (!realframe) + { + HUlib_drawTextLine(&w_coordx, false); + HUlib_drawTextLine(&w_coordy, false); + HUlib_drawTextLine(&w_coordz, false); + } + else + { + sprintf(hud_coordstrx,"X: %-5d", (plr->mo->x)>>FRACBITS); + HUlib_clearTextLine(&w_coordx); + s = hud_coordstrx; + while (*s) + HUlib_addCharToTextLine(&w_coordx, *(s++)); + HUlib_drawTextLine(&w_coordx, false); + + //jff 3/3/98 split coord display into x,y,z lines + // y-coord + sprintf(hud_coordstry,"Y: %-5d", (plr->mo->y)>>FRACBITS); + HUlib_clearTextLine(&w_coordy); + s = hud_coordstry; + while (*s) + HUlib_addCharToTextLine(&w_coordy, *(s++)); + HUlib_drawTextLine(&w_coordy, false); + + //jff 3/3/98 split coord display into x,y,z lines + //jff 2/22/98 added z + // z-coord + sprintf(hud_coordstrz,"Z: %-5d", (plr->mo->z)>>FRACBITS); + HUlib_clearTextLine(&w_coordz); + s = hud_coordstrz; + while (*s) + HUlib_addCharToTextLine(&w_coordz, *(s++)); + HUlib_drawTextLine(&w_coordz, false); + } + } + + if (map_level_stat) + { + static char str[32]; + int time = leveltime / TICRATE; + int ttime = (totalleveltimes + leveltime) / TICRATE; + + sprintf(str, "Monsters: \x1b%c%d/%d", '0' + hudcolor_mapstat_value, + players[consoleplayer].killcount - players[consoleplayer].resurectedkillcount, + totalkills); + HUlib_clearTextLine(&w_map_monsters); + s = str; + while (*s) + HUlib_addCharToTextLine(&w_map_monsters, *(s++)); + HUlib_drawTextLine(&w_map_monsters, false); + + sprintf(str, "Secrets: \x1b%c%d/%d", '0' + hudcolor_mapstat_value, + players[consoleplayer].secretcount, totalsecret); + HUlib_clearTextLine(&w_map_secrets); + s = str; + while (*s) + HUlib_addCharToTextLine(&w_map_secrets, *(s++)); + HUlib_drawTextLine(&w_map_secrets, false); + + sprintf(str, "Items: \x1b%c%d/%d", '0' + hudcolor_mapstat_value, + players[consoleplayer].itemcount, totalitems); + HUlib_clearTextLine(&w_map_items); + s = str; + while (*s) + HUlib_addCharToTextLine(&w_map_items, *(s++)); + HUlib_drawTextLine(&w_map_items, false); + + sprintf(str, "%02d:%02d:%02d", time/3600, (time%3600)/60, time%60); + HUlib_clearTextLine(&w_map_time); + s = str; + while (*s) + HUlib_addCharToTextLine(&w_map_time, *(s++)); + HUlib_drawTextLine(&w_map_time, false); + + if (totalleveltimes > 0) + { + sprintf(str, "%02d:%02d:%02d", ttime/3600, (ttime%3600)/60, ttime%60); + HUlib_clearTextLine(&w_map_totaltime); + s = str; + while (*s) + HUlib_addCharToTextLine(&w_map_totaltime, *(s++)); + HUlib_drawTextLine(&w_map_totaltime, false); + } + } + } + + // draw the weapon/health/ammo/armor/kills/keys displays if optioned + //jff 2/17/98 allow new hud stuff to be turned off + // killough 2/21/98: really allow new hud stuff to be turned off COMPLETELY + if + ( + hud_num > 0 && // hud optioned on + hud_displayed && // hud on from fullscreen key + viewheight==SCREENHEIGHT && // fullscreen mode is active + (!(automapmode & am_active) || // automap is not active + (automapmode & am_overlay)) + ) + { + int i; + + HU_MoveHud(false); // insure HUD display coords are correct + + if (hud_current) + { + for (i = 0; i < hud_current->count; i++) + { + if (hud_current->items[i].build && hud_current->items[i].draw) + { + if (realframe) + { + hud_current->items[i].build(); + } + hud_current->items[i].draw(); + } + } + } + + //e6y + if (traces_present) + { + int k, num = 0; + for(k = 0; k < NUMTRACES; k++) + { + if (traces[k].count) + { + if (realframe) + { + w_traces[num].y = w_traces[0].y - num * 8; + + if (traces[k].ApplyFunc) + traces[k].ApplyFunc(k); + + HUlib_clearTextLine(&w_traces[num]); + s = traces[k].hudstr; + while (*s) + HUlib_addCharToTextLine(&w_traces[num], *(s++)); + + if (traces[k].ResetFunc) + traces[k].ResetFunc(k); + } + HUlib_drawTextLine(&w_traces[num], false); + num++; + } + } + } + + } + // [FG] draw Time/STS widgets above status bar + else if (drawTimeSTSwidgets()) + { + HU_MoveHud(false); + + if (realframe) + { + HU_widget_build_monsec(); + HU_widget_build_hudadd(); + } + HU_widget_draw_monsec(); + HU_widget_draw_hudadd(); + } + + //jff 3/4/98 display last to give priority + HU_Erase(); // jff 4/24/98 Erase current lines before drawing current + // needed when screen not fullsize + + // Draw crosshair before messages + if (hudadd_crosshair) + HU_draw_crosshair(); + + //jff 4/21/98 if setup has disabled message list while active, turn it off + if (hud_msg_lines<=1) + message_list = false; + + // if the message review not enabled, show the standard message widget + if (!message_list) + HUlib_drawSText(&w_message); + + //e6y + if (custom_message_p->ticks > 0) + HUlib_drawTextLine(&w_centermsg, false); + + // if the message review is enabled show the scrolling message review + if (hud_msg_lines>1 && message_list) + HUlib_drawMText(&w_rtext); + + // display the interactive buffer for chat entry + HUlib_drawIText(&w_chat); +} + +// +// HU_Erase() +// +// Erase hud display lines that can be trashed by small screen display +// +// Passed nothing, returns nothing +// +void HU_Erase(void) +{ + // erase the message display or the message review display + if (!message_list) + HUlib_eraseSText(&w_message); + else + HUlib_eraseMText(&w_rtext); + + //e6y + if (custom_message_p->ticks > 0) + HUlib_eraseTextLine(&w_centermsg); + + // erase the interactive text buffer for chat entry + HUlib_eraseIText(&w_chat); + + // erase the automap title + HUlib_eraseTextLine(&w_title); +} + +// +// HU_Ticker() +// +// Update the hud displays once per frame +// +// Passed nothing, returns nothing +// +static dboolean bsdown; // Is backspace down? +static int bscounter; + +void HU_Ticker(void) +{ + int i, rc; + char c; + + // tick down message counter if message is up + if (message_counter && !--message_counter) + { + message_on = false; + message_nottobefuckedwith = false; + } + if (bsdown && bscounter++ > 9) { + HUlib_keyInIText(&w_chat, (unsigned char)key_backspace); + bscounter = 8; + } + + // if messages on, or "Messages Off" is being displayed + // this allows the notification of turning messages off to be seen + if (showMessages || message_dontfuckwithme) + { + // display message if necessary + if ((plr->message && !message_nottobefuckedwith) + || (plr->message && message_dontfuckwithme)) + { + //post the message to the message widget + HUlib_addMessageToSText(&w_message, 0, plr->message); + //jff 2/26/98 add message to refresh text widget too + HUlib_addMessageToMText(&w_rtext, 0, plr->message); + + // clear the message to avoid posting multiple times + plr->message = 0; + // note a message is displayed + message_on = true; + // start the message persistence counter + message_counter = HU_MSGTIMEOUT; + // transfer "Messages Off" exception to the "being displayed" variable + message_nottobefuckedwith = message_dontfuckwithme; + // clear the flag that "Messages Off" is being posted + message_dontfuckwithme = 0; + } + } + + // centered messages + for (i = 0; i < MAXPLAYERS; i++) + { + if (custom_message[i].ticks > 0) + custom_message[i].ticks--; + } + if (custom_message_p->msg) + { + const char *s = custom_message_p->msg; + HUlib_clearTextLine(&w_centermsg); + while (*s) + { + HUlib_addCharToTextLine(&w_centermsg, *(s++)); + } + HUlib_setTextXCenter(&w_centermsg); + w_centermsg.cm = custom_message_p->cm; + custom_message_p->msg = NULL; + + if (custom_message_p->sfx > 0 && custom_message_p->sfx < NUMSFX) + { + S_StartSound(NULL, custom_message_p->sfx); + } + } + + // check for incoming chat characters + if (netgame) + { + for (i=0; i= 'a' && c <= 'z') + c = (char) shiftxform[(unsigned char) c]; + rc = HUlib_keyInIText(&w_inputbuffer[i], c); + if (rc && c == KEYD_ENTER) + { + if (w_inputbuffer[i].l.len + && (chat_dest[i] == consoleplayer+1 + || chat_dest[i] == HU_BROADCAST)) + { + HUlib_addMessageToSText(&w_message, + player_names[i], + w_inputbuffer[i].l.l); + + message_nottobefuckedwith = true; + message_on = true; + message_counter = HU_MSGTIMEOUT; + if ( gamemode == commercial ) + S_StartSound(0, sfx_radio); + else + S_StartSound(0, sfx_tink); + } + HUlib_resetIText(&w_inputbuffer[i]); + } + } + players[i].cmd.chatchar = 0; + } + } + } +} + +#define QUEUESIZE 128 + +static char chatchars[QUEUESIZE]; +static int head = 0; +static int tail = 0; + +// +// HU_queueChatChar() +// +// Add an incoming character to the circular chat queue +// +// Passed the character to queue, returns nothing +// +static void HU_queueChatChar(char c) +{ + if (((head + 1) & (QUEUESIZE-1)) == tail) + { + plr->message = HUSTR_MSGU; + } + else + { + chatchars[head] = c; + head = (head + 1) & (QUEUESIZE-1); + } +} + +// +// HU_dequeueChatChar() +// +// Remove the earliest added character from the circular chat queue +// +// Passed nothing, returns the character dequeued +// +char HU_dequeueChatChar(void) +{ + char c; + + if (head != tail) + { + c = chatchars[tail]; + tail = (tail + 1) & (QUEUESIZE-1); + } + else + { + c = 0; + } + return c; +} + +// +// HU_Responder() +// +// Responds to input events that affect the heads up displays +// +// Passed the event to respond to, returns true if the event was handled +// +dboolean HU_Responder(event_t *ev) +{ + + static char lastmessage[HU_MAXLINELENGTH+1]; + const char* macromessage; // CPhipps - const char* + dboolean eatkey = false; + static dboolean shiftdown = false; + static dboolean altdown = false; + unsigned char c; + int i; + int numplayers; + + static int num_nobrainers = 0; + + numplayers = 0; + for (i=0 ; idata1 == key_shift) + { + shiftdown = ev->type == ev_keydown; + return false; + } + else if (ev->data1 == key_alt) + { + altdown = ev->type == ev_keydown; + return false; + } + else if (ev->data1 == key_backspace) + { + bsdown = ev->type == ev_keydown; + bscounter = 0; + } + + if (ev->type != ev_keydown) + return false; + + if (!chat_on) + { + if (ev->data1 == key_enter) // phares + { +#ifndef INSTRUMENTED // never turn on message review if INSTRUMENTED defined + if (hud_msg_lines>1) // it posts multi-line messages that will trash + { + if (message_list) HU_Erase(); //jff 4/28/98 erase behind messages + message_list = !message_list; //jff 2/26/98 toggle list of messages + } +#endif + if (!message_list) // if not message list, refresh message + { + message_on = true; + message_counter = HU_MSGTIMEOUT; + } + eatkey = true; + }//jff 2/26/98 no chat if message review is displayed + // killough 10/02/98: no chat if demo playback + // no chat in -solo-net mode + else if (!demoplayback && !message_list && netgame && numplayers > 1) + { + if (ev->data1 == key_chat) + { + eatkey = chat_on = true; + HUlib_resetIText(&w_chat); + HU_queueChatChar(HU_BROADCAST); + } + else if (numplayers > 2) + { + for (i=0; idata1 == destination_keys[i]) + { + if (playeringame[i] && i!=consoleplayer) + { + eatkey = chat_on = true; + HUlib_resetIText(&w_chat); + HU_queueChatChar((char)(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; + } + } + } + } + } + }//jff 2/26/98 no chat functions if message review is displayed + else if (!message_list) + { + c = ev->data1; + // send a macro + if (altdown) + { + c = c - '0'; + if (c > 9) + return false; + macromessage = chat_macros[c]; + + // kill last message with a '\n' + HU_queueChatChar((char)key_enter); // DEBUG!!! // phares + + // send the macro message + while (*macromessage) + HU_queueChatChar(*macromessage++); + HU_queueChatChar((char)key_enter); // phares + + // leave chat mode and notify that it was sent + chat_on = false; + strncpy(lastmessage, chat_macros[c], HU_MAXLINELENGTH); + plr->message = lastmessage; + eatkey = true; + } + else + { + if (shiftdown || (c >= 'a' && c <= 'z')) + c = shiftxform[c]; + eatkey = HUlib_keyInIText(&w_chat, c); + if (eatkey) + HU_queueChatChar(c); + + if (c == key_enter) // phares + { + chat_on = false; + if (w_chat.l.len) + { + strcpy(lastmessage, w_chat.l.l); + plr->message = lastmessage; + } + } + else if (c == key_escape) // phares + chat_on = false; + } + } + return eatkey; +} + +void T_ShowMessage (message_thinker_t* message) +{ + if (--message->delay > 0) + return; + + SetCustomMessage(message->plr, message->msg.msg, 0, + message->msg.ticks, message->msg.cm, message->msg.sfx); + + P_RemoveThinker(&message->thinker); // unlink and free +} + +int SetCustomMessage(int plr, const char *msg, int delay, int ticks, int cm, int sfx) +{ + custom_message_t item; + + if (plr < 0 || plr >= MAXPLAYERS || !msg || ticks < 0 || + sfx < 0 || sfx >= NUMSFX || cm < 0 || cm >= CR_LIMIT) + { + return false; + } + + item.msg = msg; + item.ticks = ticks; + item.cm = cm; + item.sfx = sfx; + + if (delay <= 0) + { + custom_message[plr] = item; + } + else + { + message_thinker_t *message = Z_Calloc(1, sizeof(*message), PU_LEVEL, NULL); + message->thinker.function = T_ShowMessage; + message->delay = delay; + message->plr = plr; + + message->msg = item; + + P_AddThinker(&message->thinker); + } + + return true; +} diff --git a/src/hu_stuff.h b/src/hu_stuff.h new file mode 100644 index 0000000..beb1928 --- /dev/null +++ b/src/hu_stuff.h @@ -0,0 +1,125 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: Head up display + * + *-----------------------------------------------------------------------------*/ + +#ifndef __HU_STUFF_H__ +#define __HU_STUFF_H__ + +#include "d_event.h" + +/* + * Globally visible constants. + */ +#define HU_FONTSTART '!' /* the first font characters */ +#define HU_FONTEND (0x7f) /*jff 2/16/98 '_' the last font characters */ + +/* Calculate # of glyphs in font. */ +#define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1) + +#define HU_BROADCAST 5 + +/*#define HU_MSGREFRESH KEYD_ENTER phares */ +#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) + +#define HU_CROSSHAIRS 4 +extern const char *crosshair_nam[HU_CROSSHAIRS]; +extern const char *crosshair_str[HU_CROSSHAIRS]; + +/* + * Heads up text + */ +void HU_Init(void); +void HU_LoadHUDDefs(void); +void HU_Start(void); + +dboolean HU_Responder(event_t* ev); + +void HU_Ticker(void); +void HU_Drawer(void); +char HU_dequeueChatChar(void); +void HU_Erase(void); +void HU_MoveHud(int force); // jff 3/9/98 avoid glitch in HUD display +void HU_NextHud(void); + +/* killough 5/2/98: moved from m_misc.c: */ + +/* jff 2/16/98 hud supported automap colors added */ +extern int hudcolor_titl; /* color range of automap level title */ +extern int hudcolor_xyco; /* color range of new coords on automap */ +extern int hudcolor_mapstat_title; +extern int hudcolor_mapstat_value; +extern int hudcolor_mapstat_time; +/* jff 2/16/98 hud text colors, controls added */ +extern int hudcolor_mesg; /* color range of scrolling messages */ +extern int hudcolor_chat; /* color range of chat lines */ +/* jff 2/26/98 hud message list color and background enable */ +extern int hudcolor_list; /* color of list of past messages */ +extern int hud_list_bgon; /* solid window background for list of messages */ +extern int hud_msg_lines; /* number of message lines in window up to 16 */ +/* jff 2/23/98 hud is currently displayed */ +extern int hud_displayed; /* hud is displayed */ +/* jff 2/18/98 hud/status control */ +extern int hud_num; +extern int huds_count; + +typedef struct custom_message_s +{ + int ticks; + int cm; + int sfx; + const char *msg; +} custom_message_t; + +typedef struct message_thinker_s +{ + thinker_t thinker; + int plr; + int delay; + custom_message_t msg; +} message_thinker_t; + +typedef struct crosshair_s +{ + int lump; + int w, h, flags; + int target_x, target_y, target_z, target_sprite; + float target_screen_x, target_screen_y; +} crosshair_t; +extern crosshair_t crosshair; + +int SetCustomMessage(int plr, const char *msg, int delay, int ticks, int cm, int sfx); + +#endif diff --git a/src/hu_tracers.c b/src/hu_tracers.c new file mode 100644 index 0000000..9413fc5 --- /dev/null +++ b/src/hu_tracers.c @@ -0,0 +1,305 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: tracers stuff + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomdef.h" +#include "doomstat.h" +#include "m_argv.h" +#include "m_misc.h" +#include "lprintf.h" + +#include "hu_tracers.h" + +dboolean traces_present; + +hu_textline_t w_traces[NUMTRACES]; + +void TracerApply(tracertype_t index); +void GivenDamageReset(tracertype_t index); +void GivenDamageApply(tracertype_t index); + +traceslist_t traces[NUMTRACES]; + +void InitTracers(void) +{ + int i, p; + int value, count; + + traceslistinit_t traces_init[NUMTRACES] = { + {"-trace_thingshealth", "health ", TracerApply, NULL}, + {"-trace_thingspickup", "pickup ", TracerApply, NULL}, + {"-trace_linescross" , "lcross ", TracerApply, NULL}, + {"-trace_givendamage" , "damage ", GivenDamageApply, GivenDamageReset}, + }; + + traces_present = false; + + for (i = 0; i < NUMTRACES; i++) + { + strcpy(traces[i].cmd, traces_init[i].cmd); + strcpy(traces[i].prefix, traces_init[i].prefix); + traces[i].ApplyFunc = traces_init[i].ApplyFunc; + traces[i].ResetFunc = traces_init[i].ResetFunc; + + count = 0; + traces[i].count = 0; + if ((p = M_CheckParm(traces[i].cmd)) && (p < myargc - 1)) + { + while (count < 3 && p + count < myargc - 1 && M_StrToInt(myargv[p + 1 + count], &value)) + { + sprintf(traces[i].items[count].value, "\x1b\x36%d\x1b\x33 0", value); + traces[i].items[count].index = value; + + if (traces[i].ApplyFunc) + traces[i].ApplyFunc(i); + + traces_present = true; + count++; + } + traces[i].count = count; + } + } +} + +void TracerApply(tracertype_t index) +{ + int i; + + strcpy(traces[index].hudstr, traces[index].prefix); + for (i = 0; i < traces[index].count; i++) + { + sprintf(traces[index].hudstr + strlen(traces[index].hudstr), + "\x1b\x33%s ", traces[index].items[i].value); + } +} + +void CheckThingsPickupTracer(mobj_t *mobj) +{ + if (traces[TRACE_PICKUP].count) + { + int i; + for (i = 0; i < traces[TRACE_PICKUP].count; i++) + { + if (mobj->index == traces[TRACE_PICKUP].items[i].index) + { + sprintf(traces[TRACE_PICKUP].items[i].value, + "\x1b\x36%d \x1b\x33%05.2f", + traces[TRACE_PICKUP].items[i].index, (float)(leveltime)/35); + } + } + } +} + +void CheckThingsHealthTracer(mobj_t *mobj) +{ + if (traces[TRACE_HEALTH].count) + { + int i; + for (i = 0; i < traces[TRACE_HEALTH].count; i++) + { + if (mobj->index == traces[TRACE_HEALTH].items[i].index) + { + sprintf(traces[TRACE_HEALTH].items[i].value, + "\x1b\x36%d \x1b\x33%d", + mobj->index, mobj->health); + } + } + } +} + +int crossed_lines_count = 0; +void CheckLinesCrossTracer(line_t *line) +{ + if (traces[TRACE_CROSS].count) + { + int i; + crossed_lines_count++; + for (i = 0;i < traces[TRACE_CROSS].count; i++) + { + if (line->iLineID == traces[TRACE_CROSS].items[i].index) + { + if (!traces[TRACE_CROSS].items[i].data1) + { + sprintf(traces[TRACE_CROSS].items[i].value, + "\x1b\x36%d \x1b\x33%05.2f", + traces[TRACE_CROSS].items[i].index, (float)(leveltime)/35); + traces[TRACE_CROSS].items[i].data1 = 1; + } + } + } + } +} + +void ClearLinesCrossTracer(void) +{ + if (traces[TRACE_CROSS].count) + { + if (!crossed_lines_count) + { + int i; + for (i = 0; i < traces[TRACE_CROSS].count; i++) + { + traces[TRACE_CROSS].items[i].data1 = 0; + } + } + crossed_lines_count = 0; + } +} + +static int given_damage_pertic[MAXTRACEITEMS]; +static int given_damage_pertic_saved[MAXTRACEITEMS]; +static int given_damage_total[MAXTRACEITEMS]; +static int given_damage_processed[MAXTRACEITEMS]; + +void CheckGivenDamageTracer(mobj_t *mobj, int damage) +{ + if (traces[TRACE_DAMAGE].count) + { + int i; + for (i = 0; i < traces[TRACE_DAMAGE].count; i++) + { + if (mobj->index == traces[TRACE_DAMAGE].items[i].index) + { + given_damage_processed[i] = false; + given_damage_pertic[i] += damage; + given_damage_total[i] += damage; + } + } + } +} + +void GivenDamageApply(tracertype_t index) +{ + if (traces[index].count) + { + int i; + for (i = 0; i < traces[index].count; i++) + { + if (!given_damage_processed[i]) + { + given_damage_processed[i] = true; + given_damage_pertic_saved[i] = given_damage_pertic[i]; + } + + sprintf(traces[index].items[i].value, + "\x1b\x36%d \x1b\x33%d/\x1b\x33%d", + traces[index].items[i].index, given_damage_pertic_saved[i], given_damage_total[i]); + TracerApply(index); + } + } +} + +void GivenDamageReset(tracertype_t index) +{ + int i; + for (i = 0; i < traces[index].count; i++) + { + given_damage_pertic[i] = 0; + } +} + +typedef struct { + int init_index; + int index; +} PACKEDATTR tracer_mapthing_t; + +static tracer_mapthing_t *deathmatchstarts_indexes = NULL; +static tracer_mapthing_t playerstarts_indexes[MAXPLAYERS]; +int num_deathmatchstarts_indexes = 0; + +void TracerAddDeathmatchStart(int num, int index) +{ + if (num >= num_deathmatchstarts_indexes) + { + num_deathmatchstarts_indexes = num + 1; + + deathmatchstarts_indexes = realloc( + deathmatchstarts_indexes, + num_deathmatchstarts_indexes * sizeof(deathmatchstarts_indexes[0])); + } + + deathmatchstarts_indexes[num].index = index; +} + +void TracerAddPlayerStart(int num, int index) +{ + if (traces_present) + { + //init + if (gametic == 0) + { + playerstarts_indexes[num].init_index = index; + playerstarts_indexes[num].index = index; + } + else + { + playerstarts_indexes[num].index = playerstarts_indexes[num].init_index; + } + } +} + +int TracerGetDeathmatchStart(int index) +{ + if (index >= num_deathmatchstarts_indexes) + I_Error("TracerGetDeathmatchStart: index out of bounds"); + + return deathmatchstarts_indexes[index].index; +} + +int TracerGetPlayerStart(int index) +{ + if (index >= MAXPLAYERS) + I_Error("TracerGetDeathmatchStart: index out of bounds"); + + return playerstarts_indexes[index].index; +} + +void TracerClearStarts(void) +{ + int i; + + for (i = 0; i < MAXPLAYERS; i++) + { + playerstarts_indexes[i].index = 0; + } + + num_deathmatchstarts_indexes = 0; + if (deathmatchstarts_indexes) + { + free(deathmatchstarts_indexes); + deathmatchstarts_indexes = NULL; + } +} \ No newline at end of file diff --git a/src/hu_tracers.h b/src/hu_tracers.h new file mode 100644 index 0000000..ca19d08 --- /dev/null +++ b/src/hu_tracers.h @@ -0,0 +1,97 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: tracers stuff + * + *-----------------------------------------------------------------------------*/ + +#ifndef __HU_TRACERS__ +#define __HU_TRACERS__ + +#include "hu_lib.h" + +#define MAXTRACEITEMS 8 + +typedef enum +{ + TRACE_HEALTH, + TRACE_PICKUP, + TRACE_CROSS, + TRACE_DAMAGE, + + NUMTRACES +} tracertype_t; + +typedef struct +{ + int index; + char value[16]; + int data1; +} traceitem_t; + +typedef void (*TRACERFUNC)(tracertype_t index); +typedef struct traceslist_s +{ + traceitem_t items[MAXTRACEITEMS]; + int count; + + char hudstr[80]; + char cmd[32]; + char prefix[32]; + TRACERFUNC ApplyFunc; + TRACERFUNC ResetFunc; +} traceslist_t; + +typedef struct traceslistinit_s +{ + char cmd[32]; + char prefix[32]; + TRACERFUNC ApplyFunc; + TRACERFUNC ResetFunc; +} traceslistinit_t; + +extern traceslist_t traces[]; +extern dboolean traces_present; + +extern hu_textline_t w_traces[]; + +void InitTracers(void); + +void CheckGivenDamageTracer(mobj_t *mobj, int damage); +void CheckThingsPickupTracer(mobj_t *mobj); +void CheckThingsHealthTracer(mobj_t *mobj); +void CheckLinesCrossTracer(line_t *line); +void ClearLinesCrossTracer(void); + +void TracerClearStarts(void); +void TracerAddDeathmatchStart(int num, int index); +void TracerAddPlayerStart(int num, int index); +int TracerGetDeathmatchStart(int index); +int TracerGetPlayerStart(int index); + +#endif // __HU_TRACERS__ diff --git a/src/i_capture.c b/src/i_capture.c new file mode 100644 index 0000000..509ba14 --- /dev/null +++ b/src/i_capture.c @@ -0,0 +1,645 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * + * Copyright (C) 2011 by + * Nicholai Main + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#include "SDL.h" +#include "SDL_thread.h" + +#include +#include +#include "i_sound.h" +#include "i_video.h" +#include "lprintf.h" +#include "i_system.h" +#include "i_capture.h" + +#include "m_io.h" + +int capturing_video = 0; +static const char *vid_fname; + + +typedef struct +{ // information on a running pipe + char command[PATH_MAX]; + FILE *f_stdin; + FILE *f_stdout; + FILE *f_stderr; + SDL_Thread *outthread; + const char *stdoutdumpname; + SDL_Thread *errthread; + const char *stderrdumpname; + void *user; +} pipeinfo_t; + +static pipeinfo_t soundpipe; +static pipeinfo_t videopipe; +static pipeinfo_t muxpipe; + + +const char *cap_soundcommand; +const char *cap_videocommand; +const char *cap_muxcommand; +const char *cap_tempfile1; +const char *cap_tempfile2; +int cap_remove_tempfiles; +int cap_fps; +int cap_frac; +int cap_wipescreen; + +// parses a command with simple printf-style replacements. + +// %w video width (px) +// %h video height (px) +// %s sound rate (hz) +// %f filename passed to -viddump +// %% single percent sign +// TODO: add aspect ratio information +// +// Modified to work with SDL2 resizeable window and fullscreen desktop - DTIED +static int parsecommand (char *out, const char *in, int len) +{ + int i; + + while (*in && len > 1) + { + if (*in == '%') + { + I_UpdateRenderSize(); // Handle potential resolution scaling - DTIED + switch (in[1]) + { + case 'w': + i = doom_snprintf (out, len, "%u", renderW); + break; + case 'h': + i = doom_snprintf (out, len, "%u", renderH); + break; + case 's': + i = doom_snprintf (out, len, "%u", snd_samplerate); + break; + case 'f': + i = doom_snprintf (out, len, "%s", vid_fname); + break; + case 'r': + i = doom_snprintf (out, len, "%u", cap_fps); + break; + case '%': + i = doom_snprintf (out, len, "%%"); + break; + default: + return 0; + } + out += i; + len -= i; + in += 2; + } + else + { + *out++ = *in++; + len--; + } + } + if (*in || len < 1) + { // out of space + return 0; + } + *out = 0; + return 1; +} + + + + +// popen3() implementation - +// starts a child process + +// user is a pointer to implementation defined extra data +static int my_popen3 (pipeinfo_t *p); // 1 on success +// close waits on process +static void my_pclose3 (pipeinfo_t *p); + + +#ifdef _WIN32 +// direct winapi implementation +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#include + +typedef struct +{ + HANDLE proc; + HANDLE thread; +} puser_t; + +// extra pointer is used to hold process id to wait on to close +// NB: stdin is opened as "wb", stdout, stderr as "r" +static int my_popen3 (pipeinfo_t *p) +{ + FILE *fin = NULL; + FILE *fout = NULL; + FILE *ferr = NULL; + HANDLE child_hin = INVALID_HANDLE_VALUE; + HANDLE child_hout = INVALID_HANDLE_VALUE; + HANDLE child_herr = INVALID_HANDLE_VALUE; + HANDLE parent_hin = INVALID_HANDLE_VALUE; + HANDLE parent_hout = INVALID_HANDLE_VALUE; + HANDLE parent_herr = INVALID_HANDLE_VALUE; + + + puser_t *puser = NULL; + + + PROCESS_INFORMATION piProcInfo; + STARTUPINFO siStartInfo; + SECURITY_ATTRIBUTES sa; + + puser = malloc (sizeof (puser_t)); + if (!puser) + return 0; + + puser->proc = INVALID_HANDLE_VALUE; + puser->thread = INVALID_HANDLE_VALUE; + + + // make the pipes + + sa.nLength = sizeof (sa); + sa.bInheritHandle = 1; + sa.lpSecurityDescriptor = NULL; + if (!CreatePipe (&child_hin, &parent_hin, &sa, 0)) + goto fail; + if (!CreatePipe (&parent_hout, &child_hout, &sa, 0)) + goto fail; + if (!CreatePipe (&parent_herr, &child_herr, &sa, 0)) + goto fail; + + + // very important + if (!SetHandleInformation (parent_hin, HANDLE_FLAG_INHERIT, 0)) + goto fail; + if (!SetHandleInformation (parent_hout, HANDLE_FLAG_INHERIT, 0)) + goto fail; + if (!SetHandleInformation (parent_herr, HANDLE_FLAG_INHERIT, 0)) + goto fail; + + + + + // start the child process + + ZeroMemory (&siStartInfo, sizeof (STARTUPINFO)); + siStartInfo.cb = sizeof (STARTUPINFO); + siStartInfo.hStdInput = child_hin; + siStartInfo.hStdOutput = child_hout; + siStartInfo.hStdError = child_herr; + siStartInfo.dwFlags = STARTF_USESTDHANDLES; + + if (!CreateProcess(NULL,// application name + (LPTSTR)p->command,// command line + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + DETACHED_PROCESS, // creation flags + NULL, // use parent's environment + NULL, // use parent's current directory + &siStartInfo, // STARTUPINFO pointer + &piProcInfo)) // receives PROCESS_INFORMATION + { + goto fail; + } + + + + puser->proc = piProcInfo.hProcess; + puser->thread = piProcInfo.hThread; + + + // what the hell is this cast for + if (NULL == (fin = _fdopen (_open_osfhandle ((int) parent_hin, 0), "wb"))) + goto fail; + if (NULL == (fout = _fdopen (_open_osfhandle ((int) parent_hout, 0), "r"))) + goto fail; + if (NULL == (ferr = _fdopen (_open_osfhandle ((int) parent_herr, 0), "r"))) + goto fail; + // after fdopen(osf()), we don't need to keep track of parent handles anymore + // fclose on the FILE struct will automatically free them + + + p->user = puser; + p->f_stdin = fin; + p->f_stdout = fout; + p->f_stderr = ferr; + + CloseHandle (child_hin); + CloseHandle (child_hout); + CloseHandle (child_herr); + + return 1; + + fail: + if (fin) + fclose (fin); + if (fout) + fclose (fout); + if (ferr) + fclose (ferr); + + if (puser->proc) + CloseHandle (puser->proc); + if (puser->thread) + CloseHandle (puser->thread); + + if (child_hin != INVALID_HANDLE_VALUE) + CloseHandle (child_hin); + if (child_hout != INVALID_HANDLE_VALUE) + CloseHandle (child_hout); + if (child_herr != INVALID_HANDLE_VALUE) + CloseHandle (child_herr); + if (parent_hin != INVALID_HANDLE_VALUE) + CloseHandle (parent_hin); + if (parent_hout != INVALID_HANDLE_VALUE) + CloseHandle (parent_hout); + if (parent_herr != INVALID_HANDLE_VALUE) + CloseHandle (parent_herr); + + free (puser); + + return 0; + + +} + +static void my_pclose3 (pipeinfo_t *p) +{ + puser_t *puser = (puser_t *) p->user; + + if (!p->f_stdin || !p->f_stdout || !p->f_stderr || !puser) + return; + + fclose (p->f_stdin); + //fclose (p->f_stdout); // these are closed elsewhere + //fclose (p->f_stderr); + + WaitForSingleObject (puser->proc, INFINITE); + + CloseHandle (puser->proc); + CloseHandle (puser->thread); + free (puser); +} + +#else // _WIN32 +// posix implementation +// not tested +#include +#include +#include + + +typedef struct +{ + int pid; +} puser_t; + + +static int my_popen3 (pipeinfo_t *p) +{ + FILE *fin = NULL; + FILE *fout = NULL; + FILE *ferr = NULL; + int child_hin = -1; + int child_hout = -1; + int child_herr = -1; + int parent_hin = -1; + int parent_hout = -1; + int parent_herr = -1; + + int scratch[2]; + + int pid; + + puser_t *puser = NULL; + + puser = malloc (sizeof (puser_t)); + if (!puser) + return 0; + + + + // make the pipes + if (pipe (scratch)) + goto fail; + child_hin = scratch[0]; + parent_hin = scratch[1]; + if (pipe (scratch)) + goto fail; + parent_hout = scratch[0]; + child_hout = scratch[1]; + if (pipe (scratch)) + goto fail; + parent_herr = scratch[0]; + child_herr = scratch[1]; + + pid = fork (); + + if (pid == -1) + goto fail; + if (pid == 0) + { + dup2 (child_hin, STDIN_FILENO); + dup2 (child_hout, STDOUT_FILENO); + dup2 (child_herr, STDERR_FILENO); + + close (parent_hin); + close (parent_hout); + close (parent_herr); + + // does this work? otherwise we have to parse cmd into an **argv style array + execl ("/bin/sh", "sh", "-c", p->command, NULL); + // exit forked process if command failed + _exit (0); + } + + if (NULL == (fin = fdopen (parent_hin, "wb"))) + goto fail; + if (NULL == (fout = fdopen (parent_hout, "r"))) + goto fail; + if (NULL == (ferr = fdopen (parent_herr, "r"))) + goto fail; + + close (child_hin); + close (child_hout); + close (child_herr); + + puser->pid = pid; + + p->user = puser; + p->f_stdin = fin; + p->f_stdout = fout; + p->f_stderr = ferr; + return 1; + + fail: + if (fin) + fclose (fin); + if (fout) + fclose (fout); + if (ferr) + fclose (ferr); + + close (parent_hin); + close (parent_hout); + close (parent_herr); + close (child_hin); + close (child_hout); + close (child_herr); + + free (puser); + return 0; + +} + + +static void my_pclose3 (pipeinfo_t *p) +{ + puser_t *puser = (puser_t *) p->user; + + int s; + + if (!p->f_stdin || !p->f_stdout || !p->f_stderr || !puser) + return; + + fclose (p->f_stdin); + //fclose (p->f_stdout); // these are closed elsewhere + //fclose (p->f_stderr); + + waitpid (puser->pid, &s, 0); + + free (puser); +} + + +#endif // _WIN32 + +typedef struct +{ + FILE *fin; + const char *fn; +} threaddata_t; + + +static int threadstdoutproc (void *data) +{ // simple thread proc dumps stdout + // not terribly fast + int c; + + pipeinfo_t *p = (pipeinfo_t *) data; + + FILE *f = M_fopen (p->stdoutdumpname, "w"); + + if (!f || !p->f_stdout) + return 0; + + while ((c = fgetc (p->f_stdout)) != EOF) + fputc (c, f); + + fclose (f); + fclose (p->f_stdout); + return 1; +} + +static int threadstderrproc (void *data) +{ // simple thread proc dumps stderr + // not terribly fast + int c; + + pipeinfo_t *p = (pipeinfo_t *) data; + + FILE *f = M_fopen (p->stderrdumpname, "w"); + + if (!f || !p->f_stderr) + return 0; + + while ((c = fgetc (p->f_stderr)) != EOF) + fputc (c, f); + + fclose (f); + fclose (p->f_stderr); + return 1; +} + + +// init and open sound, video pipes +// fn is filename passed from command line, typically final output file +void I_CapturePrep (const char *fn) +{ + vid_fname = fn; + + if (!parsecommand (soundpipe.command, cap_soundcommand, sizeof(soundpipe.command))) + { + lprintf (LO_ERROR, "I_CapturePrep: malformed command %s\n", cap_soundcommand); + capturing_video = 0; + return; + } + if (!parsecommand (videopipe.command, cap_videocommand, sizeof(videopipe.command))) + { + lprintf (LO_ERROR, "I_CapturePrep: malformed command %s\n", cap_videocommand); + capturing_video = 0; + return; + } + if (!parsecommand (muxpipe.command, cap_muxcommand, sizeof(muxpipe.command))) + { + lprintf (LO_ERROR, "I_CapturePrep: malformed command %s\n", cap_muxcommand); + capturing_video = 0; + return; + } + + lprintf (LO_INFO, "I_CapturePrep: opening pipe \"%s\"\n", soundpipe.command); + if (!my_popen3 (&soundpipe)) + { + lprintf (LO_ERROR, "I_CapturePrep: sound pipe failed\n"); + capturing_video = 0; + return; + } + lprintf (LO_INFO, "I_CapturePrep: opening pipe \"%s\"\n", videopipe.command); + if (!my_popen3 (&videopipe)) + { + lprintf (LO_ERROR, "I_CapturePrep: video pipe failed\n"); + my_pclose3 (&soundpipe); + capturing_video = 0; + return; + } + I_SetSoundCap (); + lprintf (LO_INFO, "I_CapturePrep: video capture started\n"); + capturing_video = 1; + + // start reader threads + soundpipe.stdoutdumpname = "sound_stdout.txt"; + soundpipe.stderrdumpname = "sound_stderr.txt"; + soundpipe.outthread = SDL_CreateThread (threadstdoutproc, "soundpipe.outthread", &soundpipe); + soundpipe.errthread = SDL_CreateThread (threadstderrproc, "soundpipe.errthread", &soundpipe); + videopipe.stdoutdumpname = "video_stdout.txt"; + videopipe.stderrdumpname = "video_stderr.txt"; + videopipe.outthread = SDL_CreateThread (threadstdoutproc, "videopipe.outthread", &videopipe); + videopipe.errthread = SDL_CreateThread (threadstderrproc, "videopipe.errthread", &videopipe); + + I_AtExit (I_CaptureFinish, true); +} + + + +// capture a single frame of video (and corresponding audio length) +// and send it to pipes +// Modified to work with SDL2 resizeable window and fullscreen desktop - DTIED +void I_CaptureFrame (void) +{ + unsigned char *snd; + unsigned char *vid; + static int partsof35 = 0; // correct for sync when samplerate % 35 != 0 + int nsampreq; + + if (!capturing_video) + return; + + nsampreq = snd_samplerate / cap_fps; + partsof35 += snd_samplerate % cap_fps; + if (partsof35 >= cap_fps) + { + partsof35 -= cap_fps; + nsampreq++; + } + + snd = I_GrabSound (nsampreq); + if (snd) + { + if (fwrite (snd, nsampreq * 4, 1, soundpipe.f_stdin) != 1) + lprintf(LO_WARN, "I_CaptureFrame: error writing soundpipe.\n"); + //free (snd); // static buffer + } + vid = I_GrabScreen (); + if (vid) + { + if (fwrite (vid, renderW * renderH * 3, 1, videopipe.f_stdin) != 1) + lprintf(LO_WARN, "I_CaptureFrame: error writing videopipe.\n"); + //free (vid); // static buffer + } + +} + + +// close pipes, call muxcommand, finalize +void I_CaptureFinish (void) +{ + int s; + + if (!capturing_video) + return; + capturing_video = 0; + + // on linux, we have to close videopipe first, because it has a copy of the write + // end of soundpipe_stdin (so that stream will never see EOF). + // is there a better way to do this? + + // (on windows, it doesn't matter what order we do it in) + my_pclose3 (&videopipe); + SDL_WaitThread (videopipe.outthread, &s); + SDL_WaitThread (videopipe.errthread, &s); + + my_pclose3 (&soundpipe); + SDL_WaitThread (soundpipe.outthread, &s); + SDL_WaitThread (soundpipe.errthread, &s); + + // muxing and temp file cleanup + + lprintf (LO_INFO, "I_CaptureFinish: opening pipe \"%s\"\n", muxpipe.command); + + if (!my_popen3 (&muxpipe)) + { + lprintf (LO_ERROR, "I_CaptureFinish: finalize pipe failed\n"); + return; + } + + muxpipe.stdoutdumpname = "mux_stdout.txt"; + muxpipe.stderrdumpname = "mux_stderr.txt"; + muxpipe.outthread = SDL_CreateThread (threadstdoutproc, "muxpipe.outthread", &muxpipe); + muxpipe.errthread = SDL_CreateThread (threadstderrproc, "muxpipe.errthread", &muxpipe); + + my_pclose3 (&muxpipe); + SDL_WaitThread (muxpipe.outthread, &s); + SDL_WaitThread (muxpipe.errthread, &s); + + + // unlink any files user wants gone + if (cap_remove_tempfiles) + { + M_remove (cap_tempfile1); + M_remove (cap_tempfile2); + } +} diff --git a/src/i_capture.h b/src/i_capture.h new file mode 100644 index 0000000..9698f4d --- /dev/null +++ b/src/i_capture.h @@ -0,0 +1,63 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * + * Copyright (C) 2011 by + * Nicholai Main + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * + *--------------------------------------------------------------------- + */ + +#ifndef __I_CAPTURE__ +#define __I_CAPTURE__ + +// commandlines passed to popen() +// this one recieves raw PCM sound on stdin +extern const char *cap_soundcommand; +// this one recieves raw RGB video on stdin +extern const char *cap_videocommand; +// this one recieves nothing on stdin and is called after the other two finish +extern const char *cap_muxcommand; +// names of two files to remove after muxcommand finishes +extern const char *cap_tempfile1; +extern const char *cap_tempfile2; +extern int cap_remove_tempfiles; +extern int cap_fps; +extern int cap_frac; +extern int cap_wipescreen; + +// true if we're capturing video +extern int capturing_video; + +// init and open sound, video pipes +// fn is filename passed from command line, typically final output file +void I_CapturePrep (const char *fn); + +// capture a single frame of video (and corresponding audio length) +// and send it to pipes +void I_CaptureFrame (void); + +// close pipes, call muxcommand, finalize +void I_CaptureFinish (void); + +#endif diff --git a/src/i_glob.c b/src/i_glob.c new file mode 100644 index 0000000..797102f --- /dev/null +++ b/src/i_glob.c @@ -0,0 +1,393 @@ +// +// 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. +// + +#include +#include +#include +#include + +#include "i_glob.h" +#include "m_misc.h" +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "lprintf.h" + +#if defined(_MSC_VER) +// For Visual C++, we need to include the win_opendir module. +#include "WIN/win_opendir.h" +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#include +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#elif 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 + +#include "m_io.h" + +#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 dboolean IsDirectory(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; + + int len = doom_snprintf(NULL, 0, "%s/%s", dir, de->d_name); + filename = malloc(len+1); + doom_snprintf(filename, len+1, "%s/%s", dir, de->d_name); + result = M_stat(filename, &sb); + free(filename); + + if (result != 0) + { + return false; + } + + return S_ISDIR(sb.st_mode); + } +} + +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; +}; + +static void FreeStringList(char **globs, int num_globs) +{ + int i; + for (i = 0; i < num_globs; ++i) + { + free(globs[i]); + } + free(globs); +} + +glob_t *I_StartMultiGlob(const char *directory, int flags, + const char *glob, ...) +{ + char **globs; + int num_globs; + glob_t *result; + va_list args; + char *directory_native; + + globs = malloc(sizeof(char *)); + if (globs == NULL) + { + return NULL; + } + globs[0] = strdup(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) + { + FreeStringList(globs, num_globs); + } + globs = new_globs; + globs[num_globs] = strdup(arg); + ++num_globs; + } + va_end(args); + + result = malloc(sizeof(glob_t)); + if (result == NULL) + { + FreeStringList(globs, num_globs); + return NULL; + } + + directory_native = M_ConvertUtf8ToSysNativeMB(directory); + + result->dir = opendir(directory_native); + if (result->dir == NULL) + { + FreeStringList(globs, num_globs); + free(result); + free(directory_native); + return NULL; + } + + result->directory = directory_native; + 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_StartGlob(const char *directory, const char *glob, int flags) +{ + return I_StartMultiGlob(directory, flags, glob, NULL); +} + +void I_EndGlob(glob_t *glob) +{ + if (glob == NULL) + { + return; + } + + FreeStringList(glob->globs, glob->num_globs); + FreeStringList(glob->filenames, glob->filenames_len); + + free(glob->directory); + free(glob->last_filename); + (void) closedir(glob->dir); + free(glob); +} + +static dboolean MatchesGlob(const char *name, const char *glob, int flags) +{ + int n, 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 (MatchesGlob(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 dboolean MatchesAnyGlob(const char *name, glob_t *glob) +{ + int i; + + for (i = 0; i < glob->num_globs; ++i) + { + if (MatchesGlob(name, glob->globs[i], glob->flags)) + { + return true; + } + } + return false; +} + +static char *NextGlob(glob_t *glob) +{ + struct dirent *de; + int len; + char *temp, *ret; + + do + { + de = readdir(glob->dir); + if (de == NULL) + { + return NULL; + } + } while (IsDirectory(glob->directory, de) + || !MatchesAnyGlob(de->d_name, glob)); + + // Return the fully-qualified path, not just the bare filename. + len = doom_snprintf(NULL, 0, "%s/%s", glob->directory, de->d_name); + temp = malloc(len+1); + doom_snprintf(temp, len+1, "%s/%s", glob->directory, de->d_name); + ret = M_ConvertSysNativeMBToUtf8(temp); + return ret; +} + +static void ReadAllFilenames(glob_t *glob) +{ + char *name; + + glob->filenames = NULL; + glob->filenames_len = 0; + glob->next_index = 0; + + for (;;) + { + name = NextGlob(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 SortFilenames(char **filenames, int len, int flags) +{ + char *pivot, *tmp; + int i, left_len, 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; + + SortFilenames(filenames, left_len, flags); + SortFilenames(&filenames[left_len + 1], len - left_len - 1, flags); +} + +const char *I_NextGlob(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 = NextGlob(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) + { + ReadAllFilenames(glob); + SortFilenames(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_StartGlob(const char *directory, const char *glob, int flags) +{ + return NULL; +} + +void I_EndGlob(glob_t *glob) +{ +} + +const char *I_NextGlob(glob_t *glob) +{ + return ""; +} + +#endif /* #ifdef NO_DIRENT_IMPLEMENTATION */ + diff --git a/src/i_glob.h b/src/i_glob.h new file mode 100644 index 0000000..f976db0 --- /dev/null +++ b/src/i_glob.h @@ -0,0 +1,44 @@ +// +// 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__ + +#define GLOB_FLAG_NOCASE 0x01 +#define GLOB_FLAG_SORTED 0x02 + +typedef struct glob_s glob_t; + +// Start reading a list of file paths from the given directory which match +// the given glob pattern. I_EndGlob() must be called on completion. +glob_t *I_StartGlob(const char *directory, const char *glob, int flags); + +// Same as I_StartGlob but multiple glob patterns can be provided. The list +// of patterns must be terminated with NULL. +glob_t *I_StartMultiGlob(const char *directory, int flags, + const char *glob, ...); + +// Finish reading file list. +void I_EndGlob(glob_t *glob); + +// Read the name of the next globbed filename. NULL is returned if there +// are no more found. +const char *I_NextGlob(glob_t *glob); + +#endif + diff --git a/src/i_joy.h b/src/i_joy.h new file mode 100644 index 0000000..839bb7f --- /dev/null +++ b/src/i_joy.h @@ -0,0 +1,49 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Joystick interface. + * + *-----------------------------------------------------------------------------*/ + +extern int joybfire; +extern int joybstrafe; +extern int joybstrafeleft; +extern int joybstraferight; +extern int joybuse; +extern int joybspeed; + +extern int joyleft; +extern int joyright; +extern int joyup; +extern int joydown; + +extern int usejoystick; + +void I_InitJoystick(void); +void I_PollJoystick(void); diff --git a/src/i_main.h b/src/i_main.h new file mode 100644 index 0000000..47969c6 --- /dev/null +++ b/src/i_main.h @@ -0,0 +1,75 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * General system functions. Signal related stuff, exit function + * prototypes, and programmable Doom clock. + * + *----------------------------------------------------------------------------- + */ + +#ifndef __I_MAIN__ +#define __I_MAIN__ + +// +// e6y: exeptions handling +// + +typedef enum +{ + EXEPTION_NONE, + EXEPTION_glFramebufferTexture2DEXT, + EXEPTION_MAX +} ExeptionsList_t; + +typedef struct +{ + const char * error_message; +} ExeptionParam_t; + +extern ExeptionParam_t ExeptionsParams[]; + +void I_ExeptionBegin(ExeptionsList_t exception_index); +void I_ExeptionEnd(void); +void I_ExeptionProcess(void); + +#if defined(_WIN32) && (defined(_MSC_VER) || defined(__INTEL_COMPILER)) +void I_Warning(const char *message, ...); +#define PRBOOM_TRY(exception_index) __try +#define PRBOOM_EXCEPT(exception_index) __except(EXCEPTION_EXECUTE_HANDLER) { I_Warning("%s", ExeptionsParams[exception_index]); } +#else +#define PRBOOM_TRY(exception_index) I_ExeptionBegin(exception_index); +#define PRBOOM_EXCEPT(exception_index) I_ExeptionEnd(); +#endif + +void I_Init(void); +void I_SafeExit(int rc); + +extern int (*I_GetTime)(void); + +#endif diff --git a/src/i_network.h b/src/i_network.h new file mode 100644 index 0000000..532941f --- /dev/null +++ b/src/i_network.h @@ -0,0 +1,74 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Low level network interface. + *-----------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef USE_SDL_NET + #include "SDL_net.h" + #define UDP_SOCKET UDPsocket + #define UDP_PACKET UDPpacket + #define AF_INET + #define UDP_CHANNEL int + extern UDP_SOCKET udp_socket; +#else + #define UDP_CHANNEL struct sockaddr +#endif + +#ifndef IPPORT_RESERVED + #define IPPORT_RESERVED 1024 +#endif + +void I_InitNetwork(void); +size_t I_GetPacket(packet_header_t* buffer, size_t buflen); +void I_SendPacket(packet_header_t* packet, size_t len); +void I_WaitForPacket(int ms); + +#ifdef USE_SDL_NET +UDP_SOCKET I_Socket(Uint16 port); +int I_ConnectToServer(const char *serv); +UDP_CHANNEL I_RegisterPlayer(IPaddress *ipaddr); +void I_UnRegisterPlayer(UDP_CHANNEL channel); +extern IPaddress sentfrom_addr; +#endif + +#ifdef AF_INET +void I_SendPacketTo(packet_header_t* packet, size_t len, UDP_CHANNEL *to); +void I_SetupSocket(int sock, int port, int family); +void I_PrintAddress(FILE* fp, UDP_CHANNEL *addr); + +extern UDP_CHANNEL sentfrom; +extern int v4socket, v6socket; +#endif + +extern size_t sentbytes, recvdbytes; diff --git a/src/i_pcsound.c b/src/i_pcsound.c new file mode 100644 index 0000000..a2af5e7 --- /dev/null +++ b/src/i_pcsound.c @@ -0,0 +1,237 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2007 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// System interface for PC speaker sound. +// +//----------------------------------------------------------------------------- + +#include "SDL.h" + +#include "doomdef.h" +#include "doomtype.h" + +#include "i_pcsound.h" +#include "i_sound.h" +#include "sounds.h" + +#include "w_wad.h" + +#include "PCSOUND/pcsound.h" + +static dboolean pcs_initialised = false; + +static SDL_mutex *sound_lock; + +static const uint8_t *current_sound_lump = NULL; +static const uint8_t *current_sound_pos = NULL; +static unsigned int current_sound_remaining = 0; +static int current_sound_handle = 0; + +static float frequencies[] = { + 0.0f, 175.00f, 180.02f, 185.01f, 190.02f, 196.02f, 202.02f, 208.01f, 214.02f, 220.02f, + 226.02f, 233.04f, 240.02f, 247.03f, 254.03f, 262.00f, 269.03f, 277.03f, 285.04f, + 294.03f, 302.07f, 311.04f, 320.05f, 330.06f, 339.06f, 349.08f, 359.06f, 370.09f, + 381.08f, 392.10f, 403.10f, 415.01f, 427.05f, 440.12f, 453.16f, 466.08f, 480.15f, + 494.07f, 508.16f, 523.09f, 539.16f, 554.19f, 571.17f, 587.19f, 604.14f, 622.09f, + 640.11f, 659.21f, 679.10f, 698.17f, 719.21f, 740.18f, 762.41f, 784.47f, 807.29f, + 831.48f, 855.32f, 880.57f, 906.67f, 932.17f, 960.69f, 988.55f, 1017.20f, 1046.64f, + 1077.85f, 1109.93f, 1141.79f, 1175.54f, 1210.12f, 1244.19f, 1281.61f, 1318.43f, + 1357.42f, 1397.16f, 1439.30f, 1480.37f, 1523.85f, 1569.97f, 1614.58f, 1661.81f, + 1711.87f, 1762.45f, 1813.34f, 1864.34f, 1921.38f, 1975.46f, 2036.14f, 2093.29f, + 2157.64f, 2217.80f, 2285.78f, 2353.41f, 2420.24f, 2490.98f, 2565.97f, 2639.77f, +}; + +#define NUM_FREQUENCIES (sizeof(frequencies) / sizeof(*frequencies)) + +void PCSCallbackFunc(int *duration, int *freq) +{ + int tone; + + *duration = 1000 / 140; + + if (SDL_LockMutex(sound_lock) < 0) + { + *freq = 0; + return; + } + + if (current_sound_lump != NULL && current_sound_remaining > 0) + { + // Read the next tone + + tone = *current_sound_pos; + + // Use the tone -> frequency lookup table. See pcspkr10.zip + // for a full discussion of this. + // Check we don't overflow the frequency table. + + if (tone < (int)NUM_FREQUENCIES) + { + *freq = (int) frequencies[tone]; + } + else + { + *freq = 0; + } + + ++current_sound_pos; + --current_sound_remaining; + } + else + { + *freq = 0; + } + + SDL_UnlockMutex(sound_lock); +} + +static dboolean CachePCSLump(int sound_id) +{ + int lumplen; + int headerlen; + + // Free the current sound lump back to the cache + + if (current_sound_lump != NULL) + { + //e6y Z_ChangeTag(current_sound_lump, PU_CACHE); + current_sound_lump = NULL; + } + + // Load from WAD + + current_sound_lump = W_CacheLumpNum(S_sfx[sound_id].lumpnum/* e6y, PU_STATIC*/); + lumplen = W_LumpLength(S_sfx[sound_id].lumpnum); + + // Read header + + if (current_sound_lump[0] != 0x00 || current_sound_lump[1] != 0x00) + { + return false; + } + + headerlen = (current_sound_lump[3] << 8) | current_sound_lump[2]; + + if (headerlen > lumplen - 4) + { + return false; + } + + // Header checks out ok + + current_sound_remaining = headerlen; + current_sound_pos = current_sound_lump + 4; + + return true; +} + +int I_PCS_StartSound(int id, + int channel, + int vol, + int sep, + int pitch, + int priority) +{ + int result; + + if (!pcs_initialised) + { + return -1; + } + + // These 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. + + if (id == sfx_posact || id == sfx_bgact || id == sfx_dmact + || id == sfx_dmpain || id == sfx_popain || id == sfx_sawidl) + { + return -1; + } + + if (SDL_LockMutex(sound_lock) < 0) + { + return -1; + } + + result = CachePCSLump(id); + + if (result) + { + current_sound_handle = channel; + } + + SDL_UnlockMutex(sound_lock); + + if (result) + { + return channel; + } + else + { + return -1; + } +} + +void I_PCS_StopSound(int handle) +{ + if (!pcs_initialised) + { + return; + } + + if (SDL_LockMutex(sound_lock) < 0) + { + return; + } + + // If this is the channel currently playing, immediately end it. + + if (current_sound_handle == handle) + { + current_sound_remaining = 0; + } + + SDL_UnlockMutex(sound_lock); +} + +int I_PCS_SoundIsPlaying(int handle) +{ + if (!pcs_initialised) + { + return false; + } + + if (handle != current_sound_handle) + { + return false; + } + + return current_sound_lump != NULL && current_sound_remaining > 0; +} + +void I_PCS_InitSound(void) +{ + pcs_initialised = PCSound_Init(PCSCallbackFunc); + + sound_lock = SDL_CreateMutex(); +} + diff --git a/src/i_pcsound.h b/src/i_pcsound.h new file mode 100644 index 0000000..601f4ee --- /dev/null +++ b/src/i_pcsound.h @@ -0,0 +1,40 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2007 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// System interface for PC speaker sound. +// +//----------------------------------------------------------------------------- + +#ifndef __I_PCSOUND_H__ +#define __I_PCSOUND_H__ + +int I_PCS_StartSound(int id, + int channel, + int vol, + int sep, + int pitch, + int priority); +void I_PCS_StopSound(int handle); +int I_PCS_SoundIsPlaying(int handle); +void I_PCS_InitSound(void); + +#endif /* #ifndef __I_PCSOUND_H__ */ + diff --git a/src/i_sound.h b/src/i_sound.h new file mode 100644 index 0000000..ad34cce --- /dev/null +++ b/src/i_sound.h @@ -0,0 +1,165 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System interface, sound. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __I_SOUND__ +#define __I_SOUND__ + +#include "sounds.h" +#include "doomtype.h" + +#define SNDSERV +#undef SNDINTR + +#ifndef SNDSERV +#include "l_soundgen.h" +#endif + +extern int snd_pcspeaker; +extern int lowpass_filter; + +// Init at program start... +void I_InitSound(void); + +// ... shut down and relase at program termination. +void I_ShutdownSound(void); + +// +// SFX I/O +// + +// Initialize channels? +void I_SetChannels(void); + +// Get raw data lump index for sound descriptor. +int I_GetSfxLumpNum (sfxinfo_t *sfxinfo); + +// Starts a sound in a particular sound channel. +int I_StartSound(int id, int channel, int vol, int sep, int pitch, int priority); + +// Stops a sound channel. +void I_StopSound(int handle); + +// Called by S_*() functions +// to see if a channel is still playing. +// Returns 0 if no longer playing, 1 if playing. +dboolean I_SoundIsPlaying(int handle); + +// Called by m_menu.c to let the quit sound play and quit right after it stops +dboolean I_AnySoundStillPlaying(void); + +// Updates the volume, separation, +// and pitch of a sound channel. +void I_UpdateSoundParams(int handle, int vol, int sep, int pitch); + +// NSM sound capture routines +// silences sound output, and instead allows sound capture to work +// call this before sound startup +void I_SetSoundCap (void); +// grabs len samples of audio (16 bit interleaved) +unsigned char *I_GrabSound (int len); + +// NSM helper routine for some of the streaming audio +void I_ResampleStream (void *dest, unsigned nsamp, void (*proc) (void *dest, unsigned nsamp), unsigned sratein, unsigned srateout); + +// +// MUSIC I/O +// +extern const char *snd_soundfont; +extern const char *snd_mididev; +extern char music_player_order[][200]; + +void I_InitMusic(void); +void I_ShutdownMusic(void); + +// Volume. +void I_SetMusicVolume(int volume); + +// PAUSE game handling. +void I_PauseSong(int handle); +void I_ResumeSong(int handle); + +// Registers a song handle to song data. +int I_RegisterSong(const void *data, size_t len); + +// cournia - tries to load a music file +int I_RegisterMusic( const char* filename, musicinfo_t *music ); + +// Called by anything that wishes to start music. +// plays a song, and when the song is done, +// starts playing it again in an endless loop. +// Horrible thing to do, considering. +void I_PlaySong(int handle, int looping); + +// Stops a song over 3 seconds. +void I_StopSong(int handle); + +// See above (register), then think backwards +void I_UnRegisterSong(int handle); + +// Allegro card support jff 1/18/98 +extern int snd_card; +extern int mus_card; +// CPhipps - put these in config file +extern int snd_samplerate; +extern int snd_samplecount; + +extern int use_experimental_music; + +extern int mus_fluidsynth_chorus; +extern int mus_fluidsynth_reverb; +extern int mus_fluidsynth_gain; // NSM fine tune fluidsynth output level +extern int mus_opl_gain; // NSM fine tune OPL output level +extern const char *mus_portmidi_reset_type; // portmidi reset type +extern int mus_portmidi_reset_delay; // portmidi delay after reset +extern int mus_portmidi_filter_sysex; // portmidi block sysex from midi files +extern int mus_portmidi_reverb_level; // portmidi reverb send level +extern int mus_portmidi_chorus_level; // portmidi chorus send level + +// prefered MIDI player +typedef enum +{ + midi_player_sdl, + midi_player_fluidsynth, + midi_player_opl2, + midi_player_portmidi, + midi_player_alsa, + + midi_player_last +} midi_player_name_t; + +extern const char *snd_midiplayer; +extern const char *midiplayers[]; + +void M_ChangeMIDIPlayer(void); + +#endif diff --git a/src/i_system.h b/src/i_system.h new file mode 100644 index 0000000..2a1ee36 --- /dev/null +++ b/src/i_system.h @@ -0,0 +1,109 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System specific interface stuff. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __I_SYSTEM__ +#define __I_SYSTEM__ + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#endif + +#include "m_fixed.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +#ifdef _MSC_VER +#define F_OK 0 /* Check for file existence */ +#define W_OK 2 /* Check for write permission */ +#define R_OK 4 /* Check for read permission */ +#endif + +extern int ms_to_next_tick; +dboolean I_StartDisplay(void); +void I_EndDisplay(void); +int I_GetTime_RealTime(void); /* killough */ +#ifndef PRBOOM_SERVER +fixed_t I_GetTimeFrac (void); +#endif + +extern int (*I_TickElapsedTime)(void); + +unsigned long I_GetRandomTimeSeed(void); /* cphipps */ + +void I_uSleep(unsigned long usecs); + +/* cphipps - I_GetVersionString + * Returns a version string in the given buffer + */ +const char* I_GetVersionString(char* buf, size_t sz); + +/* cphipps - I_SigString + * Returns a string describing a signal number + */ +const char* I_SigString(char* buf, size_t sz, int signum); + +#ifdef _WIN32 +void I_SwitchToWindow(HWND hwnd); +#endif + +// e6y +const char* I_GetTempDir(void); + +const char *I_DoomExeDir(void); // killough 2/16/98: path to executable's dir + +dboolean HasTrailingSlash(const char* dn); +char* I_FindFile(const char* wfname, const char* ext); +char* I_FindFileEx(const char* wfname, const char* ext); +const char* I_FindFile2(const char* wfname, const char* ext); + +dboolean I_FileToBuffer(const char *filename, byte **data, int *size); + +/* cph 2001/11/18 - wrapper for read(2) which deals with partial reads */ +void I_Read(int fd, void* buf, size_t sz); + +/* cph 2001/11/18 - Move W_Filelength to i_system.c */ +int I_Filelength(int handle); + +// 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) + +typedef void (*atexit_func_t)(void); +void I_AtExit(atexit_func_t func, dboolean run_if_error); + +#endif diff --git a/src/i_video.h b/src/i_video.h new file mode 100644 index 0000000..5008f9b --- /dev/null +++ b/src/i_video.h @@ -0,0 +1,120 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System specific interface stuff. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __I_VIDEO__ +#define __I_VIDEO__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef GL_DOOM +#include +#endif + +#include "doomtype.h" +#include "v_video.h" +#include "SDL.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +extern int render_vsync; +extern int render_screen_multiply; +extern int integer_scaling; + +extern SDL_Window *sdl_window; +extern SDL_Renderer *sdl_renderer; + +extern const char *screen_resolutions_list[]; +extern const char *screen_resolution; + +extern const char *sdl_video_window_pos; + +void I_PreInitGraphics(void); /* CPhipps - do stuff immediately on start */ +void I_InitScreenResolution(void); /* init resolution */ +void I_SetWindowCaption(void); /* Set the window caption */ +void I_SetWindowIcon(void); /* Set the application icon */ +void I_InitGraphics (void); +void I_UpdateVideoMode(void); +void I_ShutdownGraphics(void); + +/* Takes full 8 bit values. */ +void I_SetPalette(int pal); /* CPhipps - pass down palette number */ + +void I_UpdateNoBlit (void); +void I_FinishUpdate (void); + +int I_ScreenShot (const char *fname); +// NSM expose lower level screen data grab for vidcap +unsigned char *I_GrabScreen (void); + +/* I_StartTic + * Called by D_DoomLoop, + * called before processing each tic in a frame. + * Quick syncronous operations are performed here. + * Can call D_PostEvent. + */ +void I_StartTic (void); + +/* I_StartFrame + * Called by D_DoomLoop, + * called before processing any tics in a frame + * (just after displaying a frame). + * Time consuming syncronous operations + * are performed here (joystick reading). + * Can call D_PostEvent. + */ + +void I_StartFrame (void); + +extern int use_fullscreen; /* proff 21/05/2000 */ +extern int desired_fullscreen; //e6y +extern int exclusive_fullscreen; + +void I_UpdateRenderSize(void); // Handle potential +extern int renderW; // resolution scaling +extern int renderH; // - DTIED + +// Set the process affinity mask so that all threads +extern int process_affinity_mask; +// Priority class for the prboom-plus process +extern int process_priority; +// Use vanilla keybaord mapping +extern int vanilla_keymap; + +extern dboolean window_focused; +void UpdateGrab(void); + +#endif diff --git a/src/icon.c b/src/icon.c new file mode 100644 index 0000000..1359ddc --- /dev/null +++ b/src/icon.c @@ -0,0 +1,261 @@ +static int icon_w = 32; +static int icon_h = 32; + +static unsigned char icon_data[] = { + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0xf5, 0x00,0x00,0x00,0x3c, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0x00,0x00,0x00,0x3c, + 0x01,0x00,0x00,0xee, 0x00,0x00,0x00,0x2d, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0xff, 0x03,0x00,0x00,0xf3, 0x00,0x00,0x00,0x3c, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0x00,0x00,0x00,0x3c, 0x03,0x00,0x00,0xf3, + 0x1c,0x00,0x00,0xfe, 0x03,0x00,0x00,0xe1, 0x00,0x00,0x00,0x0c, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0xff, 0x31,0x00,0x01,0xff, 0x03,0x00,0x00,0xf3, 0x00,0x00,0x00,0x3c, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0x00,0x00,0x00,0x3c, 0x04,0x00,0x00,0xf3, 0x36,0x00,0x01,0xff, + 0x45,0x00,0x01,0xff, 0x17,0x00,0x00,0xf7, 0x03,0x00,0x00,0xab, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0xff, 0x46,0x00,0x01,0xff, 0x39,0x00,0x01,0xff, 0x04,0x00,0x00,0xf3, + 0x00,0x00,0x00,0x3c, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0x3c, 0x04,0x00,0x00,0xf3, 0x3d,0x00,0x02,0xff, 0x4e,0x00,0x02,0xff, + 0x4c,0x00,0x02,0xff, 0x47,0x00,0x01,0xff, 0x08,0x00,0x00,0xf5, 0x01,0x00,0x00,0x65, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0xff, 0x4d,0x00,0x02,0xff, 0x50,0x00,0x02,0xff, 0x3f,0x00,0x02,0xff, + 0x04,0x00,0x00,0xf3, 0x00,0x00,0x00,0x3c, 0xff,0xff,0xff,0x00, 0x00,0x00,0x00,0x3c, + 0x04,0x00,0x00,0xf3, 0x43,0x00,0x02,0xff, 0x57,0x00,0x02,0xff, 0x55,0x00,0x02,0xff, + 0x53,0x00,0x02,0xff, 0x50,0x00,0x02,0xff, 0x3f,0x00,0x02,0xff, 0x01,0x00,0x00,0xfa, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0xff, 0x53,0x00,0x02,0xff, 0x56,0x00,0x02,0xff, 0x59,0x00,0x02,0xff, + 0x46,0x00,0x02,0xff, 0x05,0x00,0x00,0xf3, 0x00,0x00,0x00,0x70, 0x05,0x00,0x00,0xf3, + 0x49,0x00,0x02,0xff, 0x5f,0x00,0x02,0xff, 0x5e,0x00,0x02,0xff, 0x5c,0x00,0x02,0xff, + 0x59,0x00,0x02,0xff, 0x56,0x00,0x02,0xff, 0x53,0x00,0x02,0xff, 0x00,0x00,0x00,0xff, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0x00,0x00,0x00,0x0d, 0x00,0x00,0x00,0xf9, + 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0xff, 0x59,0x00,0x02,0xff, 0x5c,0x00,0x02,0xff, 0x60,0x00,0x02,0xff, + 0x63,0x00,0x02,0xff, 0x4d,0x00,0x02,0xff, 0x07,0x00,0x00,0xf9, 0x4f,0x00,0x02,0xff, + 0x67,0x00,0x02,0xff, 0x66,0x00,0x02,0xff, 0x65,0x00,0x02,0xff, 0x63,0x00,0x02,0xff, + 0x60,0x00,0x02,0xff, 0x5c,0x00,0x02,0xff, 0x59,0x00,0x02,0xff, 0x00,0x00,0x00,0xff, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0x04,0x00,0x00,0xbd, + 0x2d,0x00,0x01,0xff, 0x4b,0x00,0x01,0xff, 0x50,0x00,0x02,0xff, 0x55,0x00,0x02,0xff, + 0x5a,0x00,0x02,0xff, 0x5e,0x00,0x02,0xff, 0x63,0x00,0x02,0xff, 0x66,0x00,0x02,0xff, + 0x69,0x00,0x02,0xff, 0x6b,0x00,0x02,0xff, 0x68,0x00,0x02,0xff, 0x6e,0x00,0x02,0xff, + 0x6e,0x00,0x02,0xff, 0x6d,0x00,0x02,0xff, 0x6b,0x00,0x02,0xff, 0x69,0x00,0x02,0xff, + 0x66,0x00,0x02,0xff, 0x63,0x00,0x02,0xff, 0x5e,0x00,0x02,0xff, 0x00,0x00,0x00,0xff, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0x00,0x00,0x00,0x5b, + 0x14,0x00,0x00,0xf2, 0x4f,0x00,0x02,0xff, 0x55,0x00,0x02,0xff, 0x5a,0x00,0x02,0xff, + 0x5f,0x00,0x02,0xff, 0x64,0x00,0x02,0xff, 0x68,0x00,0x02,0xff, 0x6c,0x00,0x02,0xff, + 0x70,0x00,0x02,0xff, 0x72,0x00,0x03,0xff, 0x74,0x00,0x03,0xff, 0x75,0x00,0x03,0xff, + 0x75,0x00,0x03,0xff, 0x74,0x00,0x03,0xff, 0x72,0x00,0x03,0xff, 0x70,0x00,0x02,0xff, + 0x6c,0x00,0x02,0xff, 0x68,0x00,0x02,0xff, 0x64,0x00,0x02,0xff, 0x00,0x00,0x00,0xff, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0x00,0x00,0x00,0x0e, + 0x02,0x00,0x00,0xf7, 0x4f,0x00,0x02,0xff, 0x59,0x00,0x02,0xff, 0x5e,0x00,0x02,0xff, + 0x64,0x00,0x02,0xff, 0x69,0x00,0x02,0xff, 0x6e,0x00,0x02,0xff, 0x72,0x00,0x03,0xff, + 0x76,0x00,0x03,0xff, 0x79,0x00,0x03,0xff, 0x7b,0x00,0x03,0xff, 0x7c,0x00,0x03,0xff, + 0x7c,0x00,0x03,0xff, 0x7b,0x00,0x03,0xff, 0x79,0x00,0x03,0xff, 0x76,0x00,0x03,0xff, + 0x72,0x00,0x03,0xff, 0x6e,0x00,0x02,0xff, 0x69,0x00,0x02,0xff, 0x00,0x00,0x00,0xff, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x05,0x00,0x00,0xbe, 0x38,0x00,0x01,0xff, 0x5c,0x00,0x02,0xff, 0x63,0x00,0x02,0xff, + 0x68,0x00,0x02,0xff, 0x6e,0x00,0x02,0xff, 0x73,0x00,0x03,0xff, 0x78,0x00,0x03,0xff, + 0x7c,0x00,0x03,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0x7c,0x00,0x03,0xff, + 0x78,0x00,0x03,0xff, 0x73,0x00,0x03,0xff, 0x6e,0x00,0x02,0xff, 0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xf9, 0x00,0x00,0x00,0x16, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0x5c, 0x18,0x00,0x01,0xf2, 0x60,0x00,0x02,0xff, 0x66,0x00,0x02,0xff, + 0x6c,0x00,0x02,0xff, 0x72,0x00,0x03,0xff, 0x78,0x00,0x03,0xff, 0x7d,0x00,0x03,0xff, + 0x82,0x00,0x03,0xff, 0x00,0x00,0x00,0xff, 0xf6,0xe0,0x00,0xff, 0xf5,0xde,0x00,0xff, + 0xf5,0xde,0x00,0xff, 0xf6,0xe0,0x00,0xff, 0x00,0x00,0x00,0xff, 0x82,0x00,0x03,0xff, + 0x7d,0x00,0x03,0xff, 0x78,0x00,0x03,0xff, 0x72,0x00,0x03,0xff, 0x6c,0x00,0x02,0xff, + 0x66,0x00,0x02,0xff, 0x60,0x00,0x02,0xff, 0x59,0x00,0x02,0xff, 0x53,0x00,0x02,0xff, + 0x4c,0x00,0x02,0xff, 0x22,0x00,0x00,0xfc, 0x03,0x00,0x00,0xa4, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0x3c, 0x05,0x00,0x00,0xf8, 0x63,0x00,0x02,0xff, 0x69,0x00,0x02,0xff, + 0x70,0x00,0x02,0xff, 0x76,0x00,0x03,0xff, 0x7c,0x00,0x03,0xff, 0x82,0x00,0x03,0xff, + 0x87,0x00,0x03,0xff, 0x00,0x00,0x00,0xff, 0xf0,0xd5,0x00,0xff, 0xee,0xd1,0x00,0xff, + 0xee,0xd1,0x00,0xff, 0xf0,0xd5,0x00,0xff, 0x00,0x00,0x00,0xff, 0x87,0x00,0x03,0xff, + 0x82,0x00,0x03,0xff, 0x7c,0x00,0x03,0xff, 0x76,0x00,0x03,0xff, 0x70,0x00,0x02,0xff, + 0x69,0x00,0x02,0xff, 0x63,0x00,0x02,0xff, 0x5c,0x00,0x02,0xff, 0x55,0x00,0x02,0xff, + 0x4c,0x00,0x02,0xff, 0x04,0x00,0x00,0xf6, 0x00,0x00,0x00,0x2a, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0x00,0x00,0x00,0x17, + 0x04,0x00,0x00,0xea, 0x41,0x00,0x01,0xfe, 0x65,0x00,0x02,0xff, 0x6b,0x00,0x02,0xff, + 0x72,0x00,0x03,0xff, 0x79,0x00,0x03,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0xe9,0xc9,0x00,0xff, 0xe7,0xc5,0x00,0xff, + 0xe7,0xc5,0x00,0xff, 0xe9,0xc9,0x00,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0x79,0x00,0x03,0xff, 0x72,0x00,0x03,0xff, + 0x6b,0x00,0x02,0xff, 0x65,0x00,0x02,0xff, 0x5e,0x00,0x02,0xff, 0x57,0x00,0x02,0xff, + 0x31,0x00,0x01,0xff, 0x05,0x00,0x00,0xc3, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0x00,0x00,0x00,0x07, 0x04,0x00,0x00,0xd2, + 0x2e,0x00,0x01,0xfa, 0x5f,0x00,0x02,0xff, 0x66,0x00,0x02,0xff, 0x6d,0x00,0x02,0xff, + 0x74,0x00,0x03,0xff, 0x7b,0x00,0x03,0xff, 0x00,0x00,0x00,0xff, 0xf6,0xe0,0x00,0xff, + 0xf0,0xd5,0x00,0xff, 0xe9,0xc9,0x00,0xff, 0xe4,0xc0,0x00,0xff, 0xe0,0xb9,0x00,0xff, + 0xe0,0xb9,0x00,0xff, 0xe4,0xc0,0x00,0xff, 0xe9,0xc9,0x00,0xff, 0xf0,0xd5,0x00,0xff, + 0xf6,0xe0,0x00,0xff, 0x00,0x00,0x00,0xff, 0x7b,0x00,0x03,0xff, 0x74,0x00,0x03,0xff, + 0x6d,0x00,0x02,0xff, 0x66,0x00,0x02,0xff, 0x5f,0x00,0x02,0xff, 0x58,0x00,0x02,0xff, + 0x0a,0x00,0x00,0xf3, 0x00,0x00,0x00,0x45, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0x04,0x00,0x00,0xab, 0x1c,0x00,0x01,0xf6, + 0x59,0x00,0x02,0xff, 0x60,0x00,0x02,0xff, 0x67,0x00,0x02,0xff, 0x6e,0x00,0x02,0xff, + 0x75,0x00,0x03,0xff, 0x7c,0x00,0x03,0xff, 0x00,0x00,0x00,0xff, 0xf5,0xde,0x00,0xff, + 0xee,0xd1,0x00,0xff, 0xe7,0xc5,0x00,0xff, 0xe0,0xb9,0x00,0xff, 0xda,0xae,0x00,0xff, + 0xda,0xae,0x00,0xff, 0xe0,0xb9,0x00,0xff, 0xe7,0xc5,0x00,0xff, 0xee,0xd1,0x00,0xff, + 0xf5,0xde,0x00,0xff, 0x00,0x00,0x00,0xff, 0x7c,0x00,0x03,0xff, 0x75,0x00,0x03,0xff, + 0x6e,0x00,0x02,0xff, 0x67,0x00,0x02,0xff, 0x60,0x00,0x02,0xff, 0x40,0x00,0x01,0xff, + 0x05,0x00,0x00,0xdf, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0x03,0x00,0x00,0x7d, 0x0e,0x00,0x00,0xf4, 0x50,0x00,0x02,0xff, + 0x59,0x00,0x02,0xff, 0x60,0x00,0x02,0xff, 0x67,0x00,0x02,0xff, 0x6e,0x00,0x02,0xff, + 0x75,0x00,0x03,0xff, 0x7c,0x00,0x03,0xff, 0x00,0x00,0x00,0xff, 0xf5,0xde,0x00,0xff, + 0xee,0xd1,0x00,0xff, 0xe7,0xc5,0x00,0xff, 0xe0,0xb9,0x00,0xff, 0xda,0xae,0x00,0xff, + 0xda,0xae,0x00,0xff, 0xe0,0xb9,0x00,0xff, 0xe7,0xc5,0x00,0xff, 0xee,0xd1,0x00,0xff, + 0xf5,0xde,0x00,0xff, 0x00,0x00,0x00,0xff, 0x7c,0x00,0x03,0xff, 0x75,0x00,0x03,0xff, + 0x6e,0x00,0x02,0xff, 0x67,0x00,0x02,0xff, 0x60,0x00,0x02,0xff, 0x16,0x00,0x00,0xf3, + 0x00,0x00,0x00,0x61, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x01,0x00,0x00,0x50, 0x06,0x00,0x00,0xf5, 0x43,0x00,0x01,0xff, 0x51,0x00,0x02,0xff, + 0x58,0x00,0x02,0xff, 0x5f,0x00,0x02,0xff, 0x66,0x00,0x02,0xff, 0x6d,0x00,0x02,0xff, + 0x74,0x00,0x03,0xff, 0x7b,0x00,0x03,0xff, 0x00,0x00,0x00,0xff, 0xf6,0xe0,0x00,0xff, + 0xf0,0xd5,0x00,0xff, 0xe9,0xc9,0x00,0xff, 0xe4,0xc0,0x00,0xff, 0xe0,0xb9,0x00,0xff, + 0xe0,0xb9,0x00,0xff, 0xe4,0xc0,0x00,0xff, 0xe9,0xc9,0x00,0xff, 0xf0,0xd5,0x00,0xff, + 0xf6,0xe0,0x00,0xff, 0x00,0x00,0x00,0xff, 0x7b,0x00,0x03,0xff, 0x74,0x00,0x03,0xff, + 0x6d,0x00,0x02,0xff, 0x66,0x00,0x02,0xff, 0x4f,0x00,0x02,0xff, 0x03,0x00,0x00,0xf0, + 0x00,0x00,0x00,0x07, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x01,0x00,0x00,0xf1, 0x07,0x00,0x00,0xf6, 0x21,0x00,0x00,0xfc, 0x39,0x00,0x01,0xff, + 0x53,0x00,0x02,0xff, 0x5e,0x00,0x02,0xff, 0x65,0x00,0x02,0xff, 0x6b,0x00,0x02,0xff, + 0x72,0x00,0x03,0xff, 0x79,0x00,0x03,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0xe9,0xc9,0x00,0xff, 0xe7,0xc5,0x00,0xff, + 0xe7,0xc5,0x00,0xff, 0xe9,0xc9,0x00,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0x79,0x00,0x03,0xff, 0x72,0x00,0x03,0xff, + 0x6b,0x00,0x02,0xff, 0x65,0x00,0x02,0xff, 0x23,0x00,0x01,0xf6, 0x02,0x00,0x00,0x81, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0x0c, 0x00,0x00,0x00,0x48, 0x02,0x00,0x00,0x8c, 0x05,0x00,0x00,0xd6, + 0x01,0x00,0x00,0xfe, 0x5c,0x00,0x02,0xff, 0x63,0x00,0x02,0xff, 0x69,0x00,0x02,0xff, + 0x70,0x00,0x02,0xff, 0x76,0x00,0x03,0xff, 0x7c,0x00,0x03,0xff, 0x82,0x00,0x03,0xff, + 0x87,0x00,0x03,0xff, 0x00,0x00,0x00,0xff, 0xf0,0xd5,0x00,0xff, 0xee,0xd1,0x00,0xff, + 0xee,0xd1,0x00,0xff, 0xf0,0xd5,0x00,0xff, 0x00,0x00,0x00,0xff, 0x87,0x00,0x03,0xff, + 0x82,0x00,0x03,0xff, 0x7c,0x00,0x03,0xff, 0x76,0x00,0x03,0xff, 0x70,0x00,0x02,0xff, + 0x69,0x00,0x02,0xff, 0x63,0x00,0x02,0xff, 0x04,0x00,0x00,0xf9, 0x00,0x00,0x00,0x22, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0xff, 0x59,0x00,0x02,0xff, 0x60,0x00,0x02,0xff, 0x66,0x00,0x02,0xff, + 0x6c,0x00,0x02,0xff, 0x72,0x00,0x03,0xff, 0x78,0x00,0x03,0xff, 0x7d,0x00,0x03,0xff, + 0x82,0x00,0x03,0xff, 0x00,0x00,0x00,0xff, 0xf6,0xe0,0x00,0xff, 0xf5,0xde,0x00,0xff, + 0xf5,0xde,0x00,0xff, 0xf6,0xe0,0x00,0xff, 0x00,0x00,0x00,0xff, 0x82,0x00,0x03,0xff, + 0x7d,0x00,0x03,0xff, 0x78,0x00,0x03,0xff, 0x72,0x00,0x03,0xff, 0x6c,0x00,0x02,0xff, + 0x66,0x00,0x02,0xff, 0x60,0x00,0x02,0xff, 0x1b,0x00,0x01,0xf3, 0x00,0x00,0x00,0x62, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0xff, 0x56,0x00,0x02,0xff, 0x5c,0x00,0x02,0xff, 0x63,0x00,0x02,0xff, + 0x68,0x00,0x02,0xff, 0x6e,0x00,0x02,0xff, 0x73,0x00,0x03,0xff, 0x78,0x00,0x03,0xff, + 0x7c,0x00,0x03,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0x7c,0x00,0x03,0xff, + 0x78,0x00,0x03,0xff, 0x73,0x00,0x03,0xff, 0x6e,0x00,0x02,0xff, 0x68,0x00,0x02,0xff, + 0x63,0x00,0x02,0xff, 0x5c,0x00,0x02,0xff, 0x3d,0x00,0x01,0xff, 0x06,0x00,0x00,0xce, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0xff, 0x53,0x00,0x02,0xff, 0x59,0x00,0x02,0xff, 0x5e,0x00,0x02,0xff, + 0x64,0x00,0x02,0xff, 0x69,0x00,0x02,0xff, 0x6e,0x00,0x02,0xff, 0x72,0x00,0x03,0xff, + 0x76,0x00,0x03,0xff, 0x79,0x00,0x03,0xff, 0x7b,0x00,0x03,0xff, 0x7c,0x00,0x03,0xff, + 0x7c,0x00,0x03,0xff, 0x7b,0x00,0x03,0xff, 0x79,0x00,0x03,0xff, 0x76,0x00,0x03,0xff, + 0x72,0x00,0x03,0xff, 0x6e,0x00,0x02,0xff, 0x69,0x00,0x02,0xff, 0x64,0x00,0x02,0xff, + 0x5e,0x00,0x02,0xff, 0x59,0x00,0x02,0xff, 0x52,0x00,0x02,0xff, 0x04,0x00,0x00,0xf6, + 0x00,0x00,0x00,0x1e, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0xff, 0x64,0x00,0x02,0xff, 0x68,0x00,0x02,0xff, 0x6c,0x00,0x02,0xff, + 0x70,0x00,0x02,0xff, 0x72,0x00,0x03,0xff, 0x74,0x00,0x03,0xff, 0x75,0x00,0x03,0xff, + 0x74,0x00,0x03,0xff, 0x74,0x00,0x03,0xff, 0x72,0x00,0x03,0xff, 0x70,0x00,0x02,0xff, + 0x6c,0x00,0x02,0xff, 0x68,0x00,0x02,0xff, 0x64,0x00,0x02,0xff, 0x5f,0x00,0x02,0xff, + 0x5a,0x00,0x02,0xff, 0x55,0x00,0x02,0xff, 0x4f,0x00,0x02,0xff, 0x1e,0x00,0x00,0xf7, + 0x02,0x00,0x00,0x7c, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0xff, 0x5e,0x00,0x02,0xff, 0x63,0x00,0x02,0xff, 0x66,0x00,0x02,0xff, + 0x69,0x00,0x02,0xff, 0x6b,0x00,0x02,0xff, 0x6b,0x00,0x02,0xff, 0x39,0x00,0x01,0xfb, + 0x05,0x00,0x00,0xf8, 0x27,0x00,0x01,0xf6, 0x56,0x00,0x02,0xff, 0x69,0x00,0x02,0xff, + 0x66,0x00,0x02,0xff, 0x63,0x00,0x02,0xff, 0x5e,0x00,0x02,0xff, 0x5a,0x00,0x02,0xff, + 0x55,0x00,0x02,0xff, 0x50,0x00,0x02,0xff, 0x4b,0x00,0x01,0xff, 0x37,0x00,0x01,0xff, + 0x04,0x00,0x00,0xe4, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0xff, 0x59,0x00,0x02,0xff, 0x5c,0x00,0x02,0xff, 0x60,0x00,0x02,0xff, + 0x63,0x00,0x02,0xff, 0x4e,0x00,0x02,0xff, 0x0e,0x00,0x00,0xf4, 0x06,0x00,0x00,0xc0, + 0x00,0x00,0x00,0x32, 0x03,0x00,0x00,0x7d, 0x04,0x00,0x00,0xed, 0x13,0x00,0x00,0xf2, + 0x40,0x00,0x01,0xff, 0x5b,0x00,0x02,0xff, 0x59,0x00,0x02,0xff, 0x55,0x00,0x02,0xff, + 0x50,0x00,0x02,0xff, 0x4b,0x00,0x01,0xff, 0x46,0x00,0x01,0xff, 0x41,0x00,0x01,0xff, + 0x07,0x00,0x00,0xf2, 0x00,0x00,0x00,0x34, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0xff, 0x53,0x00,0x02,0xff, 0x56,0x00,0x02,0xff, 0x53,0x00,0x02,0xff, + 0x24,0x00,0x01,0xf7, 0x04,0x00,0x00,0xee, 0x00,0x00,0x00,0x55, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0x00,0x00,0x00,0x04, 0x00,0x00,0x00,0x55, + 0x05,0x00,0x00,0xd0, 0x06,0x00,0x00,0xf5, 0x2b,0x00,0x01,0xfc, 0x49,0x00,0x02,0xff, + 0x4b,0x00,0x01,0xff, 0x46,0x00,0x01,0xff, 0x41,0x00,0x01,0xff, 0x3c,0x00,0x01,0xff, + 0x1e,0x00,0x01,0xfc, 0x03,0x00,0x00,0x99, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0xff, 0x4d,0x00,0x02,0xff, 0x34,0x00,0x01,0xff, 0x05,0x00,0x00,0xf5, + 0x04,0x00,0x00,0x9e, 0x00,0x00,0x00,0x0f, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0x00,0x00,0x00,0x31, 0x04,0x00,0x00,0xa7, 0x02,0x00,0x00,0xf7, + 0x19,0x00,0x00,0xf6, 0x35,0x00,0x01,0xff, 0x3c,0x00,0x01,0xff, 0x39,0x00,0x01,0xff, + 0x33,0x00,0x01,0xff, 0x02,0x00,0x00,0xf1, 0x00,0x00,0x00,0x04, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0xff, 0x11,0x00,0x00,0xf4, 0x04,0x00,0x00,0xdd, 0x00,0x00,0x00,0x38, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0x00,0x00,0x00,0x14, + 0x02,0x00,0x00,0x7d, 0x02,0x00,0x00,0xed, 0x0b,0x00,0x00,0xf2, 0x26,0x00,0x01,0xff, + 0x38,0x00,0x01,0xff, 0x0c,0x00,0x00,0xf0, 0x00,0x00,0x00,0x4c, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0xf8, 0x02,0x00,0x00,0x7b, 0x00,0x00,0x00,0x04, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0x00,0x00,0x00,0x04, 0x00,0x00,0x00,0x55, 0x03,0x00,0x00,0xd0, + 0x04,0x00,0x00,0xf5, 0x0d,0x00,0x00,0xfe, 0x04,0x00,0x00,0xb5, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0x21, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0x31, 0x03,0x00,0x00,0xa7, 0x01,0x00,0x00,0xf1, 0x00,0x00,0x00,0x10, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, + 0xff,0xff,0xff,0x00, 0xff,0xff,0xff,0x00, 0x00,0x00,0x00,0x13, 0x00,0x00,0x00,0x17, +}; diff --git a/src/info.c b/src/info.c new file mode 100644 index 0000000..2de38b3 --- /dev/null +++ b/src/info.c @@ -0,0 +1,5053 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Thing frame/state LUT, + * generated by multigen utilitiy. + * This one is the original DOOM version, preserved. + * BOOM changes include commenting and addition of predefined lumps + * for providing things that aren't in the IWAD without sending a + * separate must-use wad file around with the EXE. + * + *----------------------------------------------------------------------------- + */ + +#include "doomdef.h" +#include "sounds.h" +#include "m_fixed.h" +#include "p_mobj.h" +#include "p_enemy.h" +#include "p_pspr.h" + +#ifdef __GNUG__ +#pragma implementation "info.h" +#endif +#include "info.h" + +void A_BetaSkullAttack(); // killough 10/98: beta lost souls attacked different +void A_Stop(); + +// ******************************************************************** +// Sprite names +// ******************************************************************** +// This is the list of sprite 4-character prefixes. They are searched +// through, with a NULL entry terminating the list. In DOOM originally +// this NULL entry was missing, and coincidentally the next thing in +// memory was the dummy state_t[] entry that started with zero bytes. +// killough 1/17/98: add an explicit NULL entry. +// NUMSPRITES is an enum from info.h where all these are listed +// as SPR_xxxx + +const char *sprnames[NUMSPRITES+1] = { + "TROO","SHTG","PUNG","PISG","PISF","SHTF","SHT2","CHGG","CHGF","MISG", + "MISF","SAWG","PLSG","PLSF","BFGG","BFGF","BLUD","PUFF","BAL1","BAL2", + "PLSS","PLSE","MISL","BFS1","BFE1","BFE2","TFOG","IFOG","PLAY","POSS", + "SPOS","VILE","FIRE","FATB","FBXP","SKEL","MANF","FATT","CPOS","SARG", + "HEAD","BAL7","BOSS","BOS2","SKUL","SPID","BSPI","APLS","APBX","CYBR", + "PAIN","SSWV","KEEN","BBRN","BOSF","ARM1","ARM2","BAR1","BEXP","FCAN", + "BON1","BON2","BKEY","RKEY","YKEY","BSKU","RSKU","YSKU","STIM","MEDI", + "SOUL","PINV","PSTR","PINS","MEGA","SUIT","PMAP","PVIS","CLIP","AMMO", + "ROCK","BROK","CELL","CELP","SHEL","SBOX","BPAK","BFUG","MGUN","CSAW", + "LAUN","PLAS","SHOT","SGN2","COLU","SMT2","GOR1","POL2","POL5","POL4", + "POL3","POL1","POL6","GOR2","GOR3","GOR4","GOR5","SMIT","COL1","COL2", + "COL3","COL4","CAND","CBRA","COL6","TRE1","TRE2","ELEC","CEYE","FSKU", + "COL5","TBLU","TGRN","TRED","SMBT","SMGT","SMRT","HDB1","HDB2","HDB3", + "HDB4","HDB5","HDB6","POB1","POB2","BRS1","TLMP","TLP2", + "TNT1", // invisible sprite phares 3/9/98 + "DOGS", /* killough 7/19/98: Marine's best friend :) */ + "PLS1", // killough 7/19/98: first of two plasma fireballs in the beta + "PLS2", // killough 7/19/98: second of two plasma fireballs in the beta + "BON3", // killough 7/11/98: evil sceptre in the beta version + "BON4", // killough 7/11/98: unholy bible in the beta version + "BLD2", // blood splats from Doom Retro, unsused in PrBoom+ + + // 100 extra sprite names to use in dehacked patches + "SP00", "SP01", "SP02", "SP03", "SP04", "SP05", "SP06", "SP07", "SP08", "SP09", + "SP10", "SP11", "SP12", "SP13", "SP14", "SP15", "SP16", "SP17", "SP18", "SP19", + "SP20", "SP21", "SP22", "SP23", "SP24", "SP25", "SP26", "SP27", "SP28", "SP29", + "SP30", "SP31", "SP32", "SP33", "SP34", "SP35", "SP36", "SP37", "SP38", "SP39", + "SP40", "SP41", "SP42", "SP43", "SP44", "SP45", "SP46", "SP47", "SP48", "SP49", + "SP50", "SP51", "SP52", "SP53", "SP54", "SP55", "SP56", "SP57", "SP58", "SP59", + "SP60", "SP61", "SP62", "SP63", "SP64", "SP65", "SP66", "SP67", "SP68", "SP69", + "SP70", "SP71", "SP72", "SP73", "SP74", "SP75", "SP76", "SP77", "SP78", "SP79", + "SP80", "SP81", "SP82", "SP83", "SP84", "SP85", "SP86", "SP87", "SP88", "SP89", + "SP90", "SP91", "SP92", "SP93", "SP94", "SP95", "SP96", "SP97", "SP98", "SP99", + + NULL +}; + +// ******************************************************************** +// State or "frame" information +// ******************************************************************** +// Each of the states, otherwise known as "frames", is outlined +// here. The data in each element of the array is the way it is +// initialized, with sprite names identified by their enumerator +// value such as SPR_SHTG. These correlate to the above sprite +// array so don't change them around unless you understand what +// you're doing. +// +// The commented name beginning with S_ at the end of each line +// is there to help figure out where the next-frame pointer is +// pointing. These are also additionally identified in info.h +// as enumerated values. From a change-and-recompile point of +// view this is fairly workable, but it adds a lot to the effort +// when trying to change things externally. See also the d_deh.c +// parts where frame rewiring is done for more details and the +// extended way a BEX file can handle this. + +state_t states[NUMSTATES] = { + {SPR_TROO,0,-1,NULL,S_NULL,0,0}, // S_NULL + {SPR_SHTG,4,0,A_Light0,S_NULL,0,0}, // S_LIGHTDONE + {SPR_PUNG,0,1,A_WeaponReady,S_PUNCH,0,0}, // S_PUNCH + {SPR_PUNG,0,1,A_Lower,S_PUNCHDOWN,0,0}, // S_PUNCHDOWN + {SPR_PUNG,0,1,A_Raise,S_PUNCHUP,0,0}, // S_PUNCHUP + {SPR_PUNG,1,4,NULL,S_PUNCH2,0,0}, // S_PUNCH1 + {SPR_PUNG,2,4,A_Punch,S_PUNCH3,0,0}, // S_PUNCH2 + {SPR_PUNG,3,5,NULL,S_PUNCH4,0,0}, // S_PUNCH3 + {SPR_PUNG,2,4,NULL,S_PUNCH5,0,0}, // S_PUNCH4 + {SPR_PUNG,1,5,A_ReFire,S_PUNCH,0,0}, // S_PUNCH5 + {SPR_PISG,0,1,A_WeaponReady,S_PISTOL,0,0},// S_PISTOL + {SPR_PISG,0,1,A_Lower,S_PISTOLDOWN,0,0}, // S_PISTOLDOWN + {SPR_PISG,0,1,A_Raise,S_PISTOLUP,0,0}, // S_PISTOLUP + {SPR_PISG,0,4,NULL,S_PISTOL2,0,0}, // S_PISTOL1 + {SPR_PISG,1,6,A_FirePistol,S_PISTOL3,0,0},// S_PISTOL2 + {SPR_PISG,2,4,NULL,S_PISTOL4,0,0}, // S_PISTOL3 + {SPR_PISG,1,5,A_ReFire,S_PISTOL,0,0}, // S_PISTOL4 + {SPR_PISF,32768,7,A_Light1,S_LIGHTDONE,0,0}, // S_PISTOLFLASH + {SPR_SHTG,0,1,A_WeaponReady,S_SGUN,0,0}, // S_SGUN + {SPR_SHTG,0,1,A_Lower,S_SGUNDOWN,0,0}, // S_SGUNDOWN + {SPR_SHTG,0,1,A_Raise,S_SGUNUP,0,0}, // S_SGUNUP + {SPR_SHTG,0,3,NULL,S_SGUN2,0,0}, // S_SGUN1 + {SPR_SHTG,0,7,A_FireShotgun,S_SGUN3,0,0}, // S_SGUN2 + {SPR_SHTG,1,5,NULL,S_SGUN4,0,0}, // S_SGUN3 + {SPR_SHTG,2,5,NULL,S_SGUN5,0,0}, // S_SGUN4 + {SPR_SHTG,3,4,NULL,S_SGUN6,0,0}, // S_SGUN5 + {SPR_SHTG,2,5,NULL,S_SGUN7,0,0}, // S_SGUN6 + {SPR_SHTG,1,5,NULL,S_SGUN8,0,0}, // S_SGUN7 + {SPR_SHTG,0,3,NULL,S_SGUN9,0,0}, // S_SGUN8 + {SPR_SHTG,0,7,A_ReFire,S_SGUN,0,0}, // S_SGUN9 + {SPR_SHTF,32768,4,A_Light1,S_SGUNFLASH2,0,0}, // S_SGUNFLASH1 + {SPR_SHTF,32769,3,A_Light2,S_LIGHTDONE,0,0}, // S_SGUNFLASH2 + {SPR_SHT2,0,1,A_WeaponReady,S_DSGUN,0,0}, // S_DSGUN + {SPR_SHT2,0,1,A_Lower,S_DSGUNDOWN,0,0}, // S_DSGUNDOWN + {SPR_SHT2,0,1,A_Raise,S_DSGUNUP,0,0}, // S_DSGUNUP + {SPR_SHT2,0,3,NULL,S_DSGUN2,0,0}, // S_DSGUN1 + {SPR_SHT2,0,7,A_FireShotgun2,S_DSGUN3,0,0}, // S_DSGUN2 + {SPR_SHT2,1,7,NULL,S_DSGUN4,0,0}, // S_DSGUN3 + {SPR_SHT2,2,7,A_CheckReload,S_DSGUN5,0,0}, // S_DSGUN4 + {SPR_SHT2,3,7,A_OpenShotgun2,S_DSGUN6,0,0}, // S_DSGUN5 + {SPR_SHT2,4,7,NULL,S_DSGUN7,0,0}, // S_DSGUN6 + {SPR_SHT2,5,7,A_LoadShotgun2,S_DSGUN8,0,0}, // S_DSGUN7 + {SPR_SHT2,6,6,NULL,S_DSGUN9,0,0}, // S_DSGUN8 + {SPR_SHT2,7,6,A_CloseShotgun2,S_DSGUN10,0,0}, // S_DSGUN9 + {SPR_SHT2,0,5,A_ReFire,S_DSGUN,0,0}, // S_DSGUN10 + {SPR_SHT2,1,7,NULL,S_DSNR2,0,0}, // S_DSNR1 + {SPR_SHT2,0,3,NULL,S_DSGUNDOWN,0,0}, // S_DSNR2 + {SPR_SHT2,32776,5,A_Light1,S_DSGUNFLASH2,0,0}, // S_DSGUNFLASH1 + {SPR_SHT2,32777,4,A_Light2,S_LIGHTDONE,0,0}, // S_DSGUNFLASH2 + {SPR_CHGG,0,1,A_WeaponReady,S_CHAIN,0,0}, // S_CHAIN + {SPR_CHGG,0,1,A_Lower,S_CHAINDOWN,0,0}, // S_CHAINDOWN + {SPR_CHGG,0,1,A_Raise,S_CHAINUP,0,0}, // S_CHAINUP + {SPR_CHGG,0,4,A_FireCGun,S_CHAIN2,0,0}, // S_CHAIN1 + {SPR_CHGG,1,4,A_FireCGun,S_CHAIN3,0,0}, // S_CHAIN2 + {SPR_CHGG,1,0,A_ReFire,S_CHAIN,0,0}, // S_CHAIN3 + {SPR_CHGF,32768,5,A_Light1,S_LIGHTDONE,0,0}, // S_CHAINFLASH1 + {SPR_CHGF,32769,5,A_Light2,S_LIGHTDONE,0,0}, // S_CHAINFLASH2 + {SPR_MISG,0,1,A_WeaponReady,S_MISSILE,0,0}, // S_MISSILE + {SPR_MISG,0,1,A_Lower,S_MISSILEDOWN,0,0}, // S_MISSILEDOWN + {SPR_MISG,0,1,A_Raise,S_MISSILEUP,0,0}, // S_MISSILEUP + {SPR_MISG,1,8,A_GunFlash,S_MISSILE2,0,0}, // S_MISSILE1 + {SPR_MISG,1,12,A_FireMissile,S_MISSILE3,0,0}, // S_MISSILE2 + {SPR_MISG,1,0,A_ReFire,S_MISSILE,0,0}, // S_MISSILE3 + {SPR_MISF,32768,3,A_Light1,S_MISSILEFLASH2,0,0}, // S_MISSILEFLASH1 + {SPR_MISF,32769,4,NULL,S_MISSILEFLASH3,0,0}, // S_MISSILEFLASH2 + {SPR_MISF,32770,4,A_Light2,S_MISSILEFLASH4,0,0}, // S_MISSILEFLASH3 + {SPR_MISF,32771,4,A_Light2,S_LIGHTDONE,0,0}, // S_MISSILEFLASH4 + {SPR_SAWG,2,4,A_WeaponReady,S_SAWB,0,0}, // S_SAW + {SPR_SAWG,3,4,A_WeaponReady,S_SAW,0,0}, // S_SAWB + {SPR_SAWG,2,1,A_Lower,S_SAWDOWN,0,0}, // S_SAWDOWN + {SPR_SAWG,2,1,A_Raise,S_SAWUP,0,0}, // S_SAWUP + {SPR_SAWG,0,4,A_Saw,S_SAW2,0,0}, // S_SAW1 + {SPR_SAWG,1,4,A_Saw,S_SAW3,0,0}, // S_SAW2 + {SPR_SAWG,1,0,A_ReFire,S_SAW,0,0}, // S_SAW3 + {SPR_PLSG,0,1,A_WeaponReady,S_PLASMA,0,0}, // S_PLASMA + {SPR_PLSG,0,1,A_Lower,S_PLASMADOWN,0,0}, // S_PLASMADOWN + {SPR_PLSG,0,1,A_Raise,S_PLASMAUP,0,0}, // S_PLASMAUP + {SPR_PLSG,0,3,A_FirePlasma,S_PLASMA2,0,0}, // S_PLASMA1 + {SPR_PLSG,1,20,A_ReFire,S_PLASMA,0,0}, // S_PLASMA2 + {SPR_PLSF,32768,4,A_Light1,S_LIGHTDONE,0,0}, // S_PLASMAFLASH1 + {SPR_PLSF,32769,4,A_Light1,S_LIGHTDONE,0,0}, // S_PLASMAFLASH2 + {SPR_BFGG,0,1,A_WeaponReady,S_BFG,0,0}, // S_BFG + {SPR_BFGG,0,1,A_Lower,S_BFGDOWN,0,0}, // S_BFGDOWN + {SPR_BFGG,0,1,A_Raise,S_BFGUP,0,0}, // S_BFGUP + {SPR_BFGG,0,20,A_BFGsound,S_BFG2,0,0}, // S_BFG1 + {SPR_BFGG,1,10,A_GunFlash,S_BFG3,0,0}, // S_BFG2 + {SPR_BFGG,1,10,A_FireBFG,S_BFG4,0,0}, // S_BFG3 + {SPR_BFGG,1,20,A_ReFire,S_BFG,0,0}, // S_BFG4 + {SPR_BFGF,32768,11,A_Light1,S_BFGFLASH2,0,0}, // S_BFGFLASH1 + {SPR_BFGF,32769,6,A_Light2,S_LIGHTDONE,0,0}, // S_BFGFLASH2 + {SPR_BLUD,2,8,NULL,S_BLOOD2,0,0}, // S_BLOOD1 + {SPR_BLUD,1,8,NULL,S_BLOOD3,0,0}, // S_BLOOD2 + {SPR_BLUD,0,8,NULL,S_NULL,0,0}, // S_BLOOD3 + {SPR_PUFF,32768,4,NULL,S_PUFF2,0,0}, // S_PUFF1 + {SPR_PUFF,1,4,NULL,S_PUFF3,0,0}, // S_PUFF2 + {SPR_PUFF,2,4,NULL,S_PUFF4,0,0}, // S_PUFF3 + {SPR_PUFF,3,4,NULL,S_NULL,0,0}, // S_PUFF4 + {SPR_BAL1,32768,4,NULL,S_TBALL2,0,0}, // S_TBALL1 + {SPR_BAL1,32769,4,NULL,S_TBALL1,0,0}, // S_TBALL2 + {SPR_BAL1,32770,6,NULL,S_TBALLX2,0,0}, // S_TBALLX1 + {SPR_BAL1,32771,6,NULL,S_TBALLX3,0,0}, // S_TBALLX2 + {SPR_BAL1,32772,6,NULL,S_NULL,0,0}, // S_TBALLX3 + {SPR_BAL2,32768,4,NULL,S_RBALL2,0,0}, // S_RBALL1 + {SPR_BAL2,32769,4,NULL,S_RBALL1,0,0}, // S_RBALL2 + {SPR_BAL2,32770,6,NULL,S_RBALLX2,0,0}, // S_RBALLX1 + {SPR_BAL2,32771,6,NULL,S_RBALLX3,0,0}, // S_RBALLX2 + {SPR_BAL2,32772,6,NULL,S_NULL,0,0}, // S_RBALLX3 + {SPR_PLSS,32768,6,NULL,S_PLASBALL2,0,0}, // S_PLASBALL + {SPR_PLSS,32769,6,NULL,S_PLASBALL,0,0}, // S_PLASBALL2 + {SPR_PLSE,32768,4,NULL,S_PLASEXP2,0,0}, // S_PLASEXP + {SPR_PLSE,32769,4,NULL,S_PLASEXP3,0,0}, // S_PLASEXP2 + {SPR_PLSE,32770,4,NULL,S_PLASEXP4,0,0}, // S_PLASEXP3 + {SPR_PLSE,32771,4,NULL,S_PLASEXP5,0,0}, // S_PLASEXP4 + {SPR_PLSE,32772,4,NULL,S_NULL,0,0}, // S_PLASEXP5 + {SPR_MISL,32768,1,NULL,S_ROCKET,0,0}, // S_ROCKET + {SPR_BFS1,32768,4,NULL,S_BFGSHOT2,0,0}, // S_BFGSHOT + {SPR_BFS1,32769,4,NULL,S_BFGSHOT,0,0}, // S_BFGSHOT2 + {SPR_BFE1,32768,8,NULL,S_BFGLAND2,0,0}, // S_BFGLAND + {SPR_BFE1,32769,8,NULL,S_BFGLAND3,0,0}, // S_BFGLAND2 + {SPR_BFE1,32770,8,A_BFGSpray,S_BFGLAND4,0,0}, // S_BFGLAND3 + {SPR_BFE1,32771,8,NULL,S_BFGLAND5,0,0}, // S_BFGLAND4 + {SPR_BFE1,32772,8,NULL,S_BFGLAND6,0,0}, // S_BFGLAND5 + {SPR_BFE1,32773,8,NULL,S_NULL,0,0}, // S_BFGLAND6 + {SPR_BFE2,32768,8,NULL,S_BFGEXP2,0,0}, // S_BFGEXP + {SPR_BFE2,32769,8,NULL,S_BFGEXP3,0,0}, // S_BFGEXP2 + {SPR_BFE2,32770,8,NULL,S_BFGEXP4,0,0}, // S_BFGEXP3 + {SPR_BFE2,32771,8,NULL,S_NULL,0,0}, // S_BFGEXP4 + {SPR_MISL,32769,8,A_Explode,S_EXPLODE2,0,0}, // S_EXPLODE1 + {SPR_MISL,32770,6,NULL,S_EXPLODE3,0,0}, // S_EXPLODE2 + {SPR_MISL,32771,4,NULL,S_NULL,0,0}, // S_EXPLODE3 + {SPR_TFOG,32768,6,NULL,S_TFOG01,0,0}, // S_TFOG + {SPR_TFOG,32769,6,NULL,S_TFOG02,0,0}, // S_TFOG01 + {SPR_TFOG,32768,6,NULL,S_TFOG2,0,0}, // S_TFOG02 + {SPR_TFOG,32769,6,NULL,S_TFOG3,0,0}, // S_TFOG2 + {SPR_TFOG,32770,6,NULL,S_TFOG4,0,0}, // S_TFOG3 + {SPR_TFOG,32771,6,NULL,S_TFOG5,0,0}, // S_TFOG4 + {SPR_TFOG,32772,6,NULL,S_TFOG6,0,0}, // S_TFOG5 + {SPR_TFOG,32773,6,NULL,S_TFOG7,0,0}, // S_TFOG6 + {SPR_TFOG,32774,6,NULL,S_TFOG8,0,0}, // S_TFOG7 + {SPR_TFOG,32775,6,NULL,S_TFOG9,0,0}, // S_TFOG8 + {SPR_TFOG,32776,6,NULL,S_TFOG10,0,0}, // S_TFOG9 + {SPR_TFOG,32777,6,NULL,S_NULL,0,0}, // S_TFOG10 + {SPR_IFOG,32768,6,NULL,S_IFOG01,0,0}, // S_IFOG + {SPR_IFOG,32769,6,NULL,S_IFOG02,0,0}, // S_IFOG01 + {SPR_IFOG,32768,6,NULL,S_IFOG2,0,0}, // S_IFOG02 + {SPR_IFOG,32769,6,NULL,S_IFOG3,0,0}, // S_IFOG2 + {SPR_IFOG,32770,6,NULL,S_IFOG4,0,0}, // S_IFOG3 + {SPR_IFOG,32771,6,NULL,S_IFOG5,0,0}, // S_IFOG4 + {SPR_IFOG,32772,6,NULL,S_NULL,0,0}, // S_IFOG5 + {SPR_PLAY,0,-1,NULL,S_NULL,0,0}, // S_PLAY + {SPR_PLAY,0,4,NULL,S_PLAY_RUN2,0,0}, // S_PLAY_RUN1 + {SPR_PLAY,1,4,NULL,S_PLAY_RUN3,0,0}, // S_PLAY_RUN2 + {SPR_PLAY,2,4,NULL,S_PLAY_RUN4,0,0}, // S_PLAY_RUN3 + {SPR_PLAY,3,4,NULL,S_PLAY_RUN1,0,0}, // S_PLAY_RUN4 + {SPR_PLAY,4,12,NULL,S_PLAY,0,0}, // S_PLAY_ATK1 + {SPR_PLAY,32773,6,NULL,S_PLAY_ATK1,0,0}, // S_PLAY_ATK2 + {SPR_PLAY,6,4,NULL,S_PLAY_PAIN2,0,0}, // S_PLAY_PAIN + {SPR_PLAY,6,4,A_Pain,S_PLAY,0,0}, // S_PLAY_PAIN2 + {SPR_PLAY,7,10,NULL,S_PLAY_DIE2,0,0}, // S_PLAY_DIE1 + {SPR_PLAY,8,10,A_PlayerScream,S_PLAY_DIE3,0,0}, // S_PLAY_DIE2 + {SPR_PLAY,9,10,A_Fall,S_PLAY_DIE4,0,0}, // S_PLAY_DIE3 + {SPR_PLAY,10,10,NULL,S_PLAY_DIE5,0,0}, // S_PLAY_DIE4 + {SPR_PLAY,11,10,NULL,S_PLAY_DIE6,0,0}, // S_PLAY_DIE5 + {SPR_PLAY,12,10,NULL,S_PLAY_DIE7,0,0}, // S_PLAY_DIE6 + {SPR_PLAY,13,-1,NULL,S_NULL,0,0}, // S_PLAY_DIE7 + {SPR_PLAY,14,5,NULL,S_PLAY_XDIE2,0,0}, // S_PLAY_XDIE1 + {SPR_PLAY,15,5,A_XScream,S_PLAY_XDIE3,0,0}, // S_PLAY_XDIE2 + {SPR_PLAY,16,5,A_Fall,S_PLAY_XDIE4,0,0}, // S_PLAY_XDIE3 + {SPR_PLAY,17,5,NULL,S_PLAY_XDIE5,0,0}, // S_PLAY_XDIE4 + {SPR_PLAY,18,5,NULL,S_PLAY_XDIE6,0,0}, // S_PLAY_XDIE5 + {SPR_PLAY,19,5,NULL,S_PLAY_XDIE7,0,0}, // S_PLAY_XDIE6 + {SPR_PLAY,20,5,NULL,S_PLAY_XDIE8,0,0}, // S_PLAY_XDIE7 + {SPR_PLAY,21,5,NULL,S_PLAY_XDIE9,0,0}, // S_PLAY_XDIE8 + {SPR_PLAY,22,-1,NULL,S_NULL,0,0}, // S_PLAY_XDIE9 + {SPR_POSS,0,10,A_Look,S_POSS_STND2,0,0}, // S_POSS_STND + {SPR_POSS,1,10,A_Look,S_POSS_STND,0,0}, // S_POSS_STND2 + {SPR_POSS,0,4,A_Chase,S_POSS_RUN2,0,0}, // S_POSS_RUN1 + {SPR_POSS,0,4,A_Chase,S_POSS_RUN3,0,0}, // S_POSS_RUN2 + {SPR_POSS,1,4,A_Chase,S_POSS_RUN4,0,0}, // S_POSS_RUN3 + {SPR_POSS,1,4,A_Chase,S_POSS_RUN5,0,0}, // S_POSS_RUN4 + {SPR_POSS,2,4,A_Chase,S_POSS_RUN6,0,0}, // S_POSS_RUN5 + {SPR_POSS,2,4,A_Chase,S_POSS_RUN7,0,0}, // S_POSS_RUN6 + {SPR_POSS,3,4,A_Chase,S_POSS_RUN8,0,0}, // S_POSS_RUN7 + {SPR_POSS,3,4,A_Chase,S_POSS_RUN1,0,0}, // S_POSS_RUN8 + {SPR_POSS,4,10,A_FaceTarget,S_POSS_ATK2,0,0}, // S_POSS_ATK1 + {SPR_POSS,5,8,A_PosAttack,S_POSS_ATK3,0,0}, // S_POSS_ATK2 + {SPR_POSS,4,8,NULL,S_POSS_RUN1,0,0}, // S_POSS_ATK3 + {SPR_POSS,6,3,NULL,S_POSS_PAIN2,0,0}, // S_POSS_PAIN + {SPR_POSS,6,3,A_Pain,S_POSS_RUN1,0,0}, // S_POSS_PAIN2 + {SPR_POSS,7,5,NULL,S_POSS_DIE2,0,0}, // S_POSS_DIE1 + {SPR_POSS,8,5,A_Scream,S_POSS_DIE3,0,0}, // S_POSS_DIE2 + {SPR_POSS,9,5,A_Fall,S_POSS_DIE4,0,0}, // S_POSS_DIE3 + {SPR_POSS,10,5,NULL,S_POSS_DIE5,0,0}, // S_POSS_DIE4 + {SPR_POSS,11,-1,NULL,S_NULL,0,0}, // S_POSS_DIE5 + {SPR_POSS,12,5,NULL,S_POSS_XDIE2,0,0}, // S_POSS_XDIE1 + {SPR_POSS,13,5,A_XScream,S_POSS_XDIE3,0,0}, // S_POSS_XDIE2 + {SPR_POSS,14,5,A_Fall,S_POSS_XDIE4,0,0}, // S_POSS_XDIE3 + {SPR_POSS,15,5,NULL,S_POSS_XDIE5,0,0}, // S_POSS_XDIE4 + {SPR_POSS,16,5,NULL,S_POSS_XDIE6,0,0}, // S_POSS_XDIE5 + {SPR_POSS,17,5,NULL,S_POSS_XDIE7,0,0}, // S_POSS_XDIE6 + {SPR_POSS,18,5,NULL,S_POSS_XDIE8,0,0}, // S_POSS_XDIE7 + {SPR_POSS,19,5,NULL,S_POSS_XDIE9,0,0}, // S_POSS_XDIE8 + {SPR_POSS,20,-1,NULL,S_NULL,0,0}, // S_POSS_XDIE9 + {SPR_POSS,10,5,NULL,S_POSS_RAISE2,0,0}, // S_POSS_RAISE1 + {SPR_POSS,9,5,NULL,S_POSS_RAISE3,0,0}, // S_POSS_RAISE2 + {SPR_POSS,8,5,NULL,S_POSS_RAISE4,0,0}, // S_POSS_RAISE3 + {SPR_POSS,7,5,NULL,S_POSS_RUN1,0,0}, // S_POSS_RAISE4 + {SPR_SPOS,0,10,A_Look,S_SPOS_STND2,0,0}, // S_SPOS_STND + {SPR_SPOS,1,10,A_Look,S_SPOS_STND,0,0}, // S_SPOS_STND2 + {SPR_SPOS,0,3,A_Chase,S_SPOS_RUN2,0,0}, // S_SPOS_RUN1 + {SPR_SPOS,0,3,A_Chase,S_SPOS_RUN3,0,0}, // S_SPOS_RUN2 + {SPR_SPOS,1,3,A_Chase,S_SPOS_RUN4,0,0}, // S_SPOS_RUN3 + {SPR_SPOS,1,3,A_Chase,S_SPOS_RUN5,0,0}, // S_SPOS_RUN4 + {SPR_SPOS,2,3,A_Chase,S_SPOS_RUN6,0,0}, // S_SPOS_RUN5 + {SPR_SPOS,2,3,A_Chase,S_SPOS_RUN7,0,0}, // S_SPOS_RUN6 + {SPR_SPOS,3,3,A_Chase,S_SPOS_RUN8,0,0}, // S_SPOS_RUN7 + {SPR_SPOS,3,3,A_Chase,S_SPOS_RUN1,0,0}, // S_SPOS_RUN8 + {SPR_SPOS,4,10,A_FaceTarget,S_SPOS_ATK2,0,0}, // S_SPOS_ATK1 + {SPR_SPOS,32773,10,A_SPosAttack,S_SPOS_ATK3,0,0}, // S_SPOS_ATK2 + {SPR_SPOS,4,10,NULL,S_SPOS_RUN1,0,0}, // S_SPOS_ATK3 + {SPR_SPOS,6,3,NULL,S_SPOS_PAIN2,0,0}, // S_SPOS_PAIN + {SPR_SPOS,6,3,A_Pain,S_SPOS_RUN1,0,0}, // S_SPOS_PAIN2 + {SPR_SPOS,7,5,NULL,S_SPOS_DIE2,0,0}, // S_SPOS_DIE1 + {SPR_SPOS,8,5,A_Scream,S_SPOS_DIE3,0,0}, // S_SPOS_DIE2 + {SPR_SPOS,9,5,A_Fall,S_SPOS_DIE4,0,0}, // S_SPOS_DIE3 + {SPR_SPOS,10,5,NULL,S_SPOS_DIE5,0,0}, // S_SPOS_DIE4 + {SPR_SPOS,11,-1,NULL,S_NULL,0,0}, // S_SPOS_DIE5 + {SPR_SPOS,12,5,NULL,S_SPOS_XDIE2,0,0}, // S_SPOS_XDIE1 + {SPR_SPOS,13,5,A_XScream,S_SPOS_XDIE3,0,0}, // S_SPOS_XDIE2 + {SPR_SPOS,14,5,A_Fall,S_SPOS_XDIE4,0,0}, // S_SPOS_XDIE3 + {SPR_SPOS,15,5,NULL,S_SPOS_XDIE5,0,0}, // S_SPOS_XDIE4 + {SPR_SPOS,16,5,NULL,S_SPOS_XDIE6,0,0}, // S_SPOS_XDIE5 + {SPR_SPOS,17,5,NULL,S_SPOS_XDIE7,0,0}, // S_SPOS_XDIE6 + {SPR_SPOS,18,5,NULL,S_SPOS_XDIE8,0,0}, // S_SPOS_XDIE7 + {SPR_SPOS,19,5,NULL,S_SPOS_XDIE9,0,0}, // S_SPOS_XDIE8 + {SPR_SPOS,20,-1,NULL,S_NULL,0,0}, // S_SPOS_XDIE9 + {SPR_SPOS,11,5,NULL,S_SPOS_RAISE2,0,0}, // S_SPOS_RAISE1 + {SPR_SPOS,10,5,NULL,S_SPOS_RAISE3,0,0}, // S_SPOS_RAISE2 + {SPR_SPOS,9,5,NULL,S_SPOS_RAISE4,0,0}, // S_SPOS_RAISE3 + {SPR_SPOS,8,5,NULL,S_SPOS_RAISE5,0,0}, // S_SPOS_RAISE4 + {SPR_SPOS,7,5,NULL,S_SPOS_RUN1,0,0}, // S_SPOS_RAISE5 + {SPR_VILE,0,10,A_Look,S_VILE_STND2,0,0}, // S_VILE_STND + {SPR_VILE,1,10,A_Look,S_VILE_STND,0,0}, // S_VILE_STND2 + {SPR_VILE,0,2,A_VileChase,S_VILE_RUN2,0,0}, // S_VILE_RUN1 + {SPR_VILE,0,2,A_VileChase,S_VILE_RUN3,0,0}, // S_VILE_RUN2 + {SPR_VILE,1,2,A_VileChase,S_VILE_RUN4,0,0}, // S_VILE_RUN3 + {SPR_VILE,1,2,A_VileChase,S_VILE_RUN5,0,0}, // S_VILE_RUN4 + {SPR_VILE,2,2,A_VileChase,S_VILE_RUN6,0,0}, // S_VILE_RUN5 + {SPR_VILE,2,2,A_VileChase,S_VILE_RUN7,0,0}, // S_VILE_RUN6 + {SPR_VILE,3,2,A_VileChase,S_VILE_RUN8,0,0}, // S_VILE_RUN7 + {SPR_VILE,3,2,A_VileChase,S_VILE_RUN9,0,0}, // S_VILE_RUN8 + {SPR_VILE,4,2,A_VileChase,S_VILE_RUN10,0,0}, // S_VILE_RUN9 + {SPR_VILE,4,2,A_VileChase,S_VILE_RUN11,0,0}, // S_VILE_RUN10 + {SPR_VILE,5,2,A_VileChase,S_VILE_RUN12,0,0}, // S_VILE_RUN11 + {SPR_VILE,5,2,A_VileChase,S_VILE_RUN1,0,0}, // S_VILE_RUN12 + {SPR_VILE,32774,0,A_VileStart,S_VILE_ATK2,0,0}, // S_VILE_ATK1 + {SPR_VILE,32774,10,A_FaceTarget,S_VILE_ATK3,0,0}, // S_VILE_ATK2 + {SPR_VILE,32775,8,A_VileTarget,S_VILE_ATK4,0,0}, // S_VILE_ATK3 + {SPR_VILE,32776,8,A_FaceTarget,S_VILE_ATK5,0,0}, // S_VILE_ATK4 + {SPR_VILE,32777,8,A_FaceTarget,S_VILE_ATK6,0,0}, // S_VILE_ATK5 + {SPR_VILE,32778,8,A_FaceTarget,S_VILE_ATK7,0,0}, // S_VILE_ATK6 + {SPR_VILE,32779,8,A_FaceTarget,S_VILE_ATK8,0,0}, // S_VILE_ATK7 + {SPR_VILE,32780,8,A_FaceTarget,S_VILE_ATK9,0,0}, // S_VILE_ATK8 + {SPR_VILE,32781,8,A_FaceTarget,S_VILE_ATK10,0,0}, // S_VILE_ATK9 + {SPR_VILE,32782,8,A_VileAttack,S_VILE_ATK11,0,0}, // S_VILE_ATK10 + {SPR_VILE,32783,20,NULL,S_VILE_RUN1,0,0}, // S_VILE_ATK11 + {SPR_VILE,32794,10,NULL,S_VILE_HEAL2,0,0}, // S_VILE_HEAL1 + {SPR_VILE,32795,10,NULL,S_VILE_HEAL3,0,0}, // S_VILE_HEAL2 + {SPR_VILE,32796,10,NULL,S_VILE_RUN1,0,0}, // S_VILE_HEAL3 + {SPR_VILE,16,5,NULL,S_VILE_PAIN2,0,0}, // S_VILE_PAIN + {SPR_VILE,16,5,A_Pain,S_VILE_RUN1,0,0}, // S_VILE_PAIN2 + {SPR_VILE,16,7,NULL,S_VILE_DIE2,0,0}, // S_VILE_DIE1 + {SPR_VILE,17,7,A_Scream,S_VILE_DIE3,0,0}, // S_VILE_DIE2 + {SPR_VILE,18,7,A_Fall,S_VILE_DIE4,0,0}, // S_VILE_DIE3 + {SPR_VILE,19,7,NULL,S_VILE_DIE5,0,0}, // S_VILE_DIE4 + {SPR_VILE,20,7,NULL,S_VILE_DIE6,0,0}, // S_VILE_DIE5 + {SPR_VILE,21,7,NULL,S_VILE_DIE7,0,0}, // S_VILE_DIE6 + {SPR_VILE,22,7,NULL,S_VILE_DIE8,0,0}, // S_VILE_DIE7 + {SPR_VILE,23,5,NULL,S_VILE_DIE9,0,0}, // S_VILE_DIE8 + {SPR_VILE,24,5,NULL,S_VILE_DIE10,0,0}, // S_VILE_DIE9 + {SPR_VILE,25,-1,NULL,S_NULL,0,0}, // S_VILE_DIE10 + {SPR_FIRE,32768,2,A_StartFire,S_FIRE2,0,0}, // S_FIRE1 + {SPR_FIRE,32769,2,A_Fire,S_FIRE3,0,0}, // S_FIRE2 + {SPR_FIRE,32768,2,A_Fire,S_FIRE4,0,0}, // S_FIRE3 + {SPR_FIRE,32769,2,A_Fire,S_FIRE5,0,0}, // S_FIRE4 + {SPR_FIRE,32770,2,A_FireCrackle,S_FIRE6,0,0}, // S_FIRE5 + {SPR_FIRE,32769,2,A_Fire,S_FIRE7,0,0}, // S_FIRE6 + {SPR_FIRE,32770,2,A_Fire,S_FIRE8,0,0}, // S_FIRE7 + {SPR_FIRE,32769,2,A_Fire,S_FIRE9,0,0}, // S_FIRE8 + {SPR_FIRE,32770,2,A_Fire,S_FIRE10,0,0}, // S_FIRE9 + {SPR_FIRE,32771,2,A_Fire,S_FIRE11,0,0}, // S_FIRE10 + {SPR_FIRE,32770,2,A_Fire,S_FIRE12,0,0}, // S_FIRE11 + {SPR_FIRE,32771,2,A_Fire,S_FIRE13,0,0}, // S_FIRE12 + {SPR_FIRE,32770,2,A_Fire,S_FIRE14,0,0}, // S_FIRE13 + {SPR_FIRE,32771,2,A_Fire,S_FIRE15,0,0}, // S_FIRE14 + {SPR_FIRE,32772,2,A_Fire,S_FIRE16,0,0}, // S_FIRE15 + {SPR_FIRE,32771,2,A_Fire,S_FIRE17,0,0}, // S_FIRE16 + {SPR_FIRE,32772,2,A_Fire,S_FIRE18,0,0}, // S_FIRE17 + {SPR_FIRE,32771,2,A_Fire,S_FIRE19,0,0}, // S_FIRE18 + {SPR_FIRE,32772,2,A_FireCrackle,S_FIRE20,0,0}, // S_FIRE19 + {SPR_FIRE,32773,2,A_Fire,S_FIRE21,0,0}, // S_FIRE20 + {SPR_FIRE,32772,2,A_Fire,S_FIRE22,0,0}, // S_FIRE21 + {SPR_FIRE,32773,2,A_Fire,S_FIRE23,0,0}, // S_FIRE22 + {SPR_FIRE,32772,2,A_Fire,S_FIRE24,0,0}, // S_FIRE23 + {SPR_FIRE,32773,2,A_Fire,S_FIRE25,0,0}, // S_FIRE24 + {SPR_FIRE,32774,2,A_Fire,S_FIRE26,0,0}, // S_FIRE25 + {SPR_FIRE,32775,2,A_Fire,S_FIRE27,0,0}, // S_FIRE26 + {SPR_FIRE,32774,2,A_Fire,S_FIRE28,0,0}, // S_FIRE27 + {SPR_FIRE,32775,2,A_Fire,S_FIRE29,0,0}, // S_FIRE28 + {SPR_FIRE,32774,2,A_Fire,S_FIRE30,0,0}, // S_FIRE29 + {SPR_FIRE,32775,2,A_Fire,S_NULL,0,0}, // S_FIRE30 + {SPR_PUFF,1,4,NULL,S_SMOKE2,0,0}, // S_SMOKE1 + {SPR_PUFF,2,4,NULL,S_SMOKE3,0,0}, // S_SMOKE2 + {SPR_PUFF,1,4,NULL,S_SMOKE4,0,0}, // S_SMOKE3 + {SPR_PUFF,2,4,NULL,S_SMOKE5,0,0}, // S_SMOKE4 + {SPR_PUFF,3,4,NULL,S_NULL,0,0}, // S_SMOKE5 + {SPR_FATB,32768,2,A_Tracer,S_TRACER2,0,0}, // S_TRACER + {SPR_FATB,32769,2,A_Tracer,S_TRACER,0,0}, // S_TRACER2 + {SPR_FBXP,32768,8,NULL,S_TRACEEXP2,0,0}, // S_TRACEEXP1 + {SPR_FBXP,32769,6,NULL,S_TRACEEXP3,0,0}, // S_TRACEEXP2 + {SPR_FBXP,32770,4,NULL,S_NULL,0,0}, // S_TRACEEXP3 + {SPR_SKEL,0,10,A_Look,S_SKEL_STND2,0,0}, // S_SKEL_STND + {SPR_SKEL,1,10,A_Look,S_SKEL_STND,0,0}, // S_SKEL_STND2 + {SPR_SKEL,0,2,A_Chase,S_SKEL_RUN2,0,0}, // S_SKEL_RUN1 + {SPR_SKEL,0,2,A_Chase,S_SKEL_RUN3,0,0}, // S_SKEL_RUN2 + {SPR_SKEL,1,2,A_Chase,S_SKEL_RUN4,0,0}, // S_SKEL_RUN3 + {SPR_SKEL,1,2,A_Chase,S_SKEL_RUN5,0,0}, // S_SKEL_RUN4 + {SPR_SKEL,2,2,A_Chase,S_SKEL_RUN6,0,0}, // S_SKEL_RUN5 + {SPR_SKEL,2,2,A_Chase,S_SKEL_RUN7,0,0}, // S_SKEL_RUN6 + {SPR_SKEL,3,2,A_Chase,S_SKEL_RUN8,0,0}, // S_SKEL_RUN7 + {SPR_SKEL,3,2,A_Chase,S_SKEL_RUN9,0,0}, // S_SKEL_RUN8 + {SPR_SKEL,4,2,A_Chase,S_SKEL_RUN10,0,0}, // S_SKEL_RUN9 + {SPR_SKEL,4,2,A_Chase,S_SKEL_RUN11,0,0}, // S_SKEL_RUN10 + {SPR_SKEL,5,2,A_Chase,S_SKEL_RUN12,0,0}, // S_SKEL_RUN11 + {SPR_SKEL,5,2,A_Chase,S_SKEL_RUN1,0,0}, // S_SKEL_RUN12 + {SPR_SKEL,6,0,A_FaceTarget,S_SKEL_FIST2,0,0}, // S_SKEL_FIST1 + {SPR_SKEL,6,6,A_SkelWhoosh,S_SKEL_FIST3,0,0}, // S_SKEL_FIST2 + {SPR_SKEL,7,6,A_FaceTarget,S_SKEL_FIST4,0,0}, // S_SKEL_FIST3 + {SPR_SKEL,8,6,A_SkelFist,S_SKEL_RUN1,0,0}, // S_SKEL_FIST4 + {SPR_SKEL,32777,0,A_FaceTarget,S_SKEL_MISS2,0,0}, // S_SKEL_MISS1 + {SPR_SKEL,32777,10,A_FaceTarget,S_SKEL_MISS3,0,0}, // S_SKEL_MISS2 + {SPR_SKEL,10,10,A_SkelMissile,S_SKEL_MISS4,0,0}, // S_SKEL_MISS3 + {SPR_SKEL,10,10,A_FaceTarget,S_SKEL_RUN1,0,0}, // S_SKEL_MISS4 + {SPR_SKEL,11,5,NULL,S_SKEL_PAIN2,0,0}, // S_SKEL_PAIN + {SPR_SKEL,11,5,A_Pain,S_SKEL_RUN1,0,0}, // S_SKEL_PAIN2 + {SPR_SKEL,11,7,NULL,S_SKEL_DIE2,0,0}, // S_SKEL_DIE1 + {SPR_SKEL,12,7,NULL,S_SKEL_DIE3,0,0}, // S_SKEL_DIE2 + {SPR_SKEL,13,7,A_Scream,S_SKEL_DIE4,0,0}, // S_SKEL_DIE3 + {SPR_SKEL,14,7,A_Fall,S_SKEL_DIE5,0,0}, // S_SKEL_DIE4 + {SPR_SKEL,15,7,NULL,S_SKEL_DIE6,0,0}, // S_SKEL_DIE5 + {SPR_SKEL,16,-1,NULL,S_NULL,0,0}, // S_SKEL_DIE6 + {SPR_SKEL,16,5,NULL,S_SKEL_RAISE2,0,0}, // S_SKEL_RAISE1 + {SPR_SKEL,15,5,NULL,S_SKEL_RAISE3,0,0}, // S_SKEL_RAISE2 + {SPR_SKEL,14,5,NULL,S_SKEL_RAISE4,0,0}, // S_SKEL_RAISE3 + {SPR_SKEL,13,5,NULL,S_SKEL_RAISE5,0,0}, // S_SKEL_RAISE4 + {SPR_SKEL,12,5,NULL,S_SKEL_RAISE6,0,0}, // S_SKEL_RAISE5 + {SPR_SKEL,11,5,NULL,S_SKEL_RUN1,0,0}, // S_SKEL_RAISE6 + {SPR_MANF,32768,4,NULL,S_FATSHOT2,0,0}, // S_FATSHOT1 + {SPR_MANF,32769,4,NULL,S_FATSHOT1,0,0}, // S_FATSHOT2 + {SPR_MISL,32769,8,NULL,S_FATSHOTX2,0,0}, // S_FATSHOTX1 + {SPR_MISL,32770,6,NULL,S_FATSHOTX3,0,0}, // S_FATSHOTX2 + {SPR_MISL,32771,4,NULL,S_NULL,0,0}, // S_FATSHOTX3 + {SPR_FATT,0,15,A_Look,S_FATT_STND2,0,0}, // S_FATT_STND + {SPR_FATT,1,15,A_Look,S_FATT_STND,0,0}, // S_FATT_STND2 + {SPR_FATT,0,4,A_Chase,S_FATT_RUN2,0,0}, // S_FATT_RUN1 + {SPR_FATT,0,4,A_Chase,S_FATT_RUN3,0,0}, // S_FATT_RUN2 + {SPR_FATT,1,4,A_Chase,S_FATT_RUN4,0,0}, // S_FATT_RUN3 + {SPR_FATT,1,4,A_Chase,S_FATT_RUN5,0,0}, // S_FATT_RUN4 + {SPR_FATT,2,4,A_Chase,S_FATT_RUN6,0,0}, // S_FATT_RUN5 + {SPR_FATT,2,4,A_Chase,S_FATT_RUN7,0,0}, // S_FATT_RUN6 + {SPR_FATT,3,4,A_Chase,S_FATT_RUN8,0,0}, // S_FATT_RUN7 + {SPR_FATT,3,4,A_Chase,S_FATT_RUN9,0,0}, // S_FATT_RUN8 + {SPR_FATT,4,4,A_Chase,S_FATT_RUN10,0,0}, // S_FATT_RUN9 + {SPR_FATT,4,4,A_Chase,S_FATT_RUN11,0,0}, // S_FATT_RUN10 + {SPR_FATT,5,4,A_Chase,S_FATT_RUN12,0,0}, // S_FATT_RUN11 + {SPR_FATT,5,4,A_Chase,S_FATT_RUN1,0,0}, // S_FATT_RUN12 + {SPR_FATT,6,20,A_FatRaise,S_FATT_ATK2,0,0}, // S_FATT_ATK1 + {SPR_FATT,32775,10,A_FatAttack1,S_FATT_ATK3,0,0}, // S_FATT_ATK2 + {SPR_FATT,8,5,A_FaceTarget,S_FATT_ATK4,0,0}, // S_FATT_ATK3 + {SPR_FATT,6,5,A_FaceTarget,S_FATT_ATK5,0,0}, // S_FATT_ATK4 + {SPR_FATT,32775,10,A_FatAttack2,S_FATT_ATK6,0,0}, // S_FATT_ATK5 + {SPR_FATT,8,5,A_FaceTarget,S_FATT_ATK7,0,0}, // S_FATT_ATK6 + {SPR_FATT,6,5,A_FaceTarget,S_FATT_ATK8,0,0}, // S_FATT_ATK7 + {SPR_FATT,32775,10,A_FatAttack3,S_FATT_ATK9,0,0}, // S_FATT_ATK8 + {SPR_FATT,8,5,A_FaceTarget,S_FATT_ATK10,0,0}, // S_FATT_ATK9 + {SPR_FATT,6,5,A_FaceTarget,S_FATT_RUN1,0,0}, // S_FATT_ATK10 + {SPR_FATT,9,3,NULL,S_FATT_PAIN2,0,0}, // S_FATT_PAIN + {SPR_FATT,9,3,A_Pain,S_FATT_RUN1,0,0}, // S_FATT_PAIN2 + {SPR_FATT,10,6,NULL,S_FATT_DIE2,0,0}, // S_FATT_DIE1 + {SPR_FATT,11,6,A_Scream,S_FATT_DIE3,0,0}, // S_FATT_DIE2 + {SPR_FATT,12,6,A_Fall,S_FATT_DIE4,0,0}, // S_FATT_DIE3 + {SPR_FATT,13,6,NULL,S_FATT_DIE5,0,0}, // S_FATT_DIE4 + {SPR_FATT,14,6,NULL,S_FATT_DIE6,0,0}, // S_FATT_DIE5 + {SPR_FATT,15,6,NULL,S_FATT_DIE7,0,0}, // S_FATT_DIE6 + {SPR_FATT,16,6,NULL,S_FATT_DIE8,0,0}, // S_FATT_DIE7 + {SPR_FATT,17,6,NULL,S_FATT_DIE9,0,0}, // S_FATT_DIE8 + {SPR_FATT,18,6,NULL,S_FATT_DIE10,0,0}, // S_FATT_DIE9 + {SPR_FATT,19,-1,A_BossDeath,S_NULL,0,0}, // S_FATT_DIE10 + {SPR_FATT,17,5,NULL,S_FATT_RAISE2,0,0}, // S_FATT_RAISE1 + {SPR_FATT,16,5,NULL,S_FATT_RAISE3,0,0}, // S_FATT_RAISE2 + {SPR_FATT,15,5,NULL,S_FATT_RAISE4,0,0}, // S_FATT_RAISE3 + {SPR_FATT,14,5,NULL,S_FATT_RAISE5,0,0}, // S_FATT_RAISE4 + {SPR_FATT,13,5,NULL,S_FATT_RAISE6,0,0}, // S_FATT_RAISE5 + {SPR_FATT,12,5,NULL,S_FATT_RAISE7,0,0}, // S_FATT_RAISE6 + {SPR_FATT,11,5,NULL,S_FATT_RAISE8,0,0}, // S_FATT_RAISE7 + {SPR_FATT,10,5,NULL,S_FATT_RUN1,0,0}, // S_FATT_RAISE8 + {SPR_CPOS,0,10,A_Look,S_CPOS_STND2,0,0}, // S_CPOS_STND + {SPR_CPOS,1,10,A_Look,S_CPOS_STND,0,0}, // S_CPOS_STND2 + {SPR_CPOS,0,3,A_Chase,S_CPOS_RUN2,0,0}, // S_CPOS_RUN1 + {SPR_CPOS,0,3,A_Chase,S_CPOS_RUN3,0,0}, // S_CPOS_RUN2 + {SPR_CPOS,1,3,A_Chase,S_CPOS_RUN4,0,0}, // S_CPOS_RUN3 + {SPR_CPOS,1,3,A_Chase,S_CPOS_RUN5,0,0}, // S_CPOS_RUN4 + {SPR_CPOS,2,3,A_Chase,S_CPOS_RUN6,0,0}, // S_CPOS_RUN5 + {SPR_CPOS,2,3,A_Chase,S_CPOS_RUN7,0,0}, // S_CPOS_RUN6 + {SPR_CPOS,3,3,A_Chase,S_CPOS_RUN8,0,0}, // S_CPOS_RUN7 + {SPR_CPOS,3,3,A_Chase,S_CPOS_RUN1,0,0}, // S_CPOS_RUN8 + {SPR_CPOS,4,10,A_FaceTarget,S_CPOS_ATK2,0,0}, // S_CPOS_ATK1 + {SPR_CPOS,32773,4,A_CPosAttack,S_CPOS_ATK3,0,0}, // S_CPOS_ATK2 + {SPR_CPOS,32772,4,A_CPosAttack,S_CPOS_ATK4,0,0}, // S_CPOS_ATK3 + {SPR_CPOS,5,1,A_CPosRefire,S_CPOS_ATK2,0,0}, // S_CPOS_ATK4 + {SPR_CPOS,6,3,NULL,S_CPOS_PAIN2,0,0}, // S_CPOS_PAIN + {SPR_CPOS,6,3,A_Pain,S_CPOS_RUN1,0,0}, // S_CPOS_PAIN2 + {SPR_CPOS,7,5,NULL,S_CPOS_DIE2,0,0}, // S_CPOS_DIE1 + {SPR_CPOS,8,5,A_Scream,S_CPOS_DIE3,0,0}, // S_CPOS_DIE2 + {SPR_CPOS,9,5,A_Fall,S_CPOS_DIE4,0,0}, // S_CPOS_DIE3 + {SPR_CPOS,10,5,NULL,S_CPOS_DIE5,0,0}, // S_CPOS_DIE4 + {SPR_CPOS,11,5,NULL,S_CPOS_DIE6,0,0}, // S_CPOS_DIE5 + {SPR_CPOS,12,5,NULL,S_CPOS_DIE7,0,0}, // S_CPOS_DIE6 + {SPR_CPOS,13,-1,NULL,S_NULL,0,0}, // S_CPOS_DIE7 + {SPR_CPOS,14,5,NULL,S_CPOS_XDIE2,0,0}, // S_CPOS_XDIE1 + {SPR_CPOS,15,5,A_XScream,S_CPOS_XDIE3,0,0}, // S_CPOS_XDIE2 + {SPR_CPOS,16,5,A_Fall,S_CPOS_XDIE4,0,0}, // S_CPOS_XDIE3 + {SPR_CPOS,17,5,NULL,S_CPOS_XDIE5,0,0}, // S_CPOS_XDIE4 + {SPR_CPOS,18,5,NULL,S_CPOS_XDIE6,0,0}, // S_CPOS_XDIE5 + {SPR_CPOS,19,-1,NULL,S_NULL,0,0}, // S_CPOS_XDIE6 + {SPR_CPOS,13,5,NULL,S_CPOS_RAISE2,0,0}, // S_CPOS_RAISE1 + {SPR_CPOS,12,5,NULL,S_CPOS_RAISE3,0,0}, // S_CPOS_RAISE2 + {SPR_CPOS,11,5,NULL,S_CPOS_RAISE4,0,0}, // S_CPOS_RAISE3 + {SPR_CPOS,10,5,NULL,S_CPOS_RAISE5,0,0}, // S_CPOS_RAISE4 + {SPR_CPOS,9,5,NULL,S_CPOS_RAISE6,0,0}, // S_CPOS_RAISE5 + {SPR_CPOS,8,5,NULL,S_CPOS_RAISE7,0,0}, // S_CPOS_RAISE6 + {SPR_CPOS,7,5,NULL,S_CPOS_RUN1,0,0}, // S_CPOS_RAISE7 + {SPR_TROO,0,10,A_Look,S_TROO_STND2,0,0}, // S_TROO_STND + {SPR_TROO,1,10,A_Look,S_TROO_STND,0,0}, // S_TROO_STND2 + {SPR_TROO,0,3,A_Chase,S_TROO_RUN2,0,0}, // S_TROO_RUN1 + {SPR_TROO,0,3,A_Chase,S_TROO_RUN3,0,0}, // S_TROO_RUN2 + {SPR_TROO,1,3,A_Chase,S_TROO_RUN4,0,0}, // S_TROO_RUN3 + {SPR_TROO,1,3,A_Chase,S_TROO_RUN5,0,0}, // S_TROO_RUN4 + {SPR_TROO,2,3,A_Chase,S_TROO_RUN6,0,0}, // S_TROO_RUN5 + {SPR_TROO,2,3,A_Chase,S_TROO_RUN7,0,0}, // S_TROO_RUN6 + {SPR_TROO,3,3,A_Chase,S_TROO_RUN8,0,0}, // S_TROO_RUN7 + {SPR_TROO,3,3,A_Chase,S_TROO_RUN1,0,0}, // S_TROO_RUN8 + {SPR_TROO,4,8,A_FaceTarget,S_TROO_ATK2,0,0}, // S_TROO_ATK1 + {SPR_TROO,5,8,A_FaceTarget,S_TROO_ATK3,0,0}, // S_TROO_ATK2 + {SPR_TROO,6,6,A_TroopAttack,S_TROO_RUN1,0,0}, // S_TROO_ATK3 + {SPR_TROO,7,2,NULL,S_TROO_PAIN2,0,0}, // S_TROO_PAIN + {SPR_TROO,7,2,A_Pain,S_TROO_RUN1,0,0}, // S_TROO_PAIN2 + {SPR_TROO,8,8,NULL,S_TROO_DIE2,0,0}, // S_TROO_DIE1 + {SPR_TROO,9,8,A_Scream,S_TROO_DIE3,0,0}, // S_TROO_DIE2 + {SPR_TROO,10,6,NULL,S_TROO_DIE4,0,0}, // S_TROO_DIE3 + {SPR_TROO,11,6,A_Fall,S_TROO_DIE5,0,0}, // S_TROO_DIE4 + {SPR_TROO,12,-1,NULL,S_NULL,0,0}, // S_TROO_DIE5 + {SPR_TROO,13,5,NULL,S_TROO_XDIE2,0,0}, // S_TROO_XDIE1 + {SPR_TROO,14,5,A_XScream,S_TROO_XDIE3,0,0}, // S_TROO_XDIE2 + {SPR_TROO,15,5,NULL,S_TROO_XDIE4,0,0}, // S_TROO_XDIE3 + {SPR_TROO,16,5,A_Fall,S_TROO_XDIE5,0,0}, // S_TROO_XDIE4 + {SPR_TROO,17,5,NULL,S_TROO_XDIE6,0,0}, // S_TROO_XDIE5 + {SPR_TROO,18,5,NULL,S_TROO_XDIE7,0,0}, // S_TROO_XDIE6 + {SPR_TROO,19,5,NULL,S_TROO_XDIE8,0,0}, // S_TROO_XDIE7 + {SPR_TROO,20,-1,NULL,S_NULL,0,0}, // S_TROO_XDIE8 + {SPR_TROO,12,8,NULL,S_TROO_RAISE2,0,0}, // S_TROO_RAISE1 + {SPR_TROO,11,8,NULL,S_TROO_RAISE3,0,0}, // S_TROO_RAISE2 + {SPR_TROO,10,6,NULL,S_TROO_RAISE4,0,0}, // S_TROO_RAISE3 + {SPR_TROO,9,6,NULL,S_TROO_RAISE5,0,0}, // S_TROO_RAISE4 + {SPR_TROO,8,6,NULL,S_TROO_RUN1,0,0}, // S_TROO_RAISE5 + {SPR_SARG,0,10,A_Look,S_SARG_STND2,0,0}, // S_SARG_STND + {SPR_SARG,1,10,A_Look,S_SARG_STND,0,0}, // S_SARG_STND2 + {SPR_SARG,0,2,A_Chase,S_SARG_RUN2,0,0}, // S_SARG_RUN1 + {SPR_SARG,0,2,A_Chase,S_SARG_RUN3,0,0}, // S_SARG_RUN2 + {SPR_SARG,1,2,A_Chase,S_SARG_RUN4,0,0}, // S_SARG_RUN3 + {SPR_SARG,1,2,A_Chase,S_SARG_RUN5,0,0}, // S_SARG_RUN4 + {SPR_SARG,2,2,A_Chase,S_SARG_RUN6,0,0}, // S_SARG_RUN5 + {SPR_SARG,2,2,A_Chase,S_SARG_RUN7,0,0}, // S_SARG_RUN6 + {SPR_SARG,3,2,A_Chase,S_SARG_RUN8,0,0}, // S_SARG_RUN7 + {SPR_SARG,3,2,A_Chase,S_SARG_RUN1,0,0}, // S_SARG_RUN8 + {SPR_SARG,4,8,A_FaceTarget,S_SARG_ATK2,0,0}, // S_SARG_ATK1 + {SPR_SARG,5,8,A_FaceTarget,S_SARG_ATK3,0,0}, // S_SARG_ATK2 + {SPR_SARG,6,8,A_SargAttack,S_SARG_RUN1,0,0}, // S_SARG_ATK3 + {SPR_SARG,7,2,NULL,S_SARG_PAIN2,0,0}, // S_SARG_PAIN + {SPR_SARG,7,2,A_Pain,S_SARG_RUN1,0,0}, // S_SARG_PAIN2 + {SPR_SARG,8,8,NULL,S_SARG_DIE2,0,0}, // S_SARG_DIE1 + {SPR_SARG,9,8,A_Scream,S_SARG_DIE3,0,0}, // S_SARG_DIE2 + {SPR_SARG,10,4,NULL,S_SARG_DIE4,0,0}, // S_SARG_DIE3 + {SPR_SARG,11,4,A_Fall,S_SARG_DIE5,0,0}, // S_SARG_DIE4 + {SPR_SARG,12,4,NULL,S_SARG_DIE6,0,0}, // S_SARG_DIE5 + {SPR_SARG,13,-1,NULL,S_NULL,0,0}, // S_SARG_DIE6 + {SPR_SARG,13,5,NULL,S_SARG_RAISE2,0,0}, // S_SARG_RAISE1 + {SPR_SARG,12,5,NULL,S_SARG_RAISE3,0,0}, // S_SARG_RAISE2 + {SPR_SARG,11,5,NULL,S_SARG_RAISE4,0,0}, // S_SARG_RAISE3 + {SPR_SARG,10,5,NULL,S_SARG_RAISE5,0,0}, // S_SARG_RAISE4 + {SPR_SARG,9,5,NULL,S_SARG_RAISE6,0,0}, // S_SARG_RAISE5 + {SPR_SARG,8,5,NULL,S_SARG_RUN1,0,0}, // S_SARG_RAISE6 + {SPR_HEAD,0,10,A_Look,S_HEAD_STND,0,0}, // S_HEAD_STND + {SPR_HEAD,0,3,A_Chase,S_HEAD_RUN1,0,0}, // S_HEAD_RUN1 + {SPR_HEAD,1,5,A_FaceTarget,S_HEAD_ATK2,0,0}, // S_HEAD_ATK1 + {SPR_HEAD,2,5,A_FaceTarget,S_HEAD_ATK3,0,0}, // S_HEAD_ATK2 + {SPR_HEAD,32771,5,A_HeadAttack,S_HEAD_RUN1,0,0}, // S_HEAD_ATK3 + {SPR_HEAD,4,3,NULL,S_HEAD_PAIN2,0,0}, // S_HEAD_PAIN + {SPR_HEAD,4,3,A_Pain,S_HEAD_PAIN3,0,0}, // S_HEAD_PAIN2 + {SPR_HEAD,5,6,NULL,S_HEAD_RUN1,0,0}, // S_HEAD_PAIN3 + {SPR_HEAD,6,8,NULL,S_HEAD_DIE2,0,0}, // S_HEAD_DIE1 + {SPR_HEAD,7,8,A_Scream,S_HEAD_DIE3,0,0}, // S_HEAD_DIE2 + {SPR_HEAD,8,8,NULL,S_HEAD_DIE4,0,0}, // S_HEAD_DIE3 + {SPR_HEAD,9,8,NULL,S_HEAD_DIE5,0,0}, // S_HEAD_DIE4 + {SPR_HEAD,10,8,A_Fall,S_HEAD_DIE6,0,0}, // S_HEAD_DIE5 + {SPR_HEAD,11,-1,NULL,S_NULL,0,0}, // S_HEAD_DIE6 + {SPR_HEAD,11,8,NULL,S_HEAD_RAISE2,0,0}, // S_HEAD_RAISE1 + {SPR_HEAD,10,8,NULL,S_HEAD_RAISE3,0,0}, // S_HEAD_RAISE2 + {SPR_HEAD,9,8,NULL,S_HEAD_RAISE4,0,0}, // S_HEAD_RAISE3 + {SPR_HEAD,8,8,NULL,S_HEAD_RAISE5,0,0}, // S_HEAD_RAISE4 + {SPR_HEAD,7,8,NULL,S_HEAD_RAISE6,0,0}, // S_HEAD_RAISE5 + {SPR_HEAD,6,8,NULL,S_HEAD_RUN1,0,0}, // S_HEAD_RAISE6 + {SPR_BAL7,32768,4,NULL,S_BRBALL2,0,0}, // S_BRBALL1 + {SPR_BAL7,32769,4,NULL,S_BRBALL1,0,0}, // S_BRBALL2 + {SPR_BAL7,32770,6,NULL,S_BRBALLX2,0,0}, // S_BRBALLX1 + {SPR_BAL7,32771,6,NULL,S_BRBALLX3,0,0}, // S_BRBALLX2 + {SPR_BAL7,32772,6,NULL,S_NULL,0,0}, // S_BRBALLX3 + {SPR_BOSS,0,10,A_Look,S_BOSS_STND2,0,0}, // S_BOSS_STND + {SPR_BOSS,1,10,A_Look,S_BOSS_STND,0,0}, // S_BOSS_STND2 + {SPR_BOSS,0,3,A_Chase,S_BOSS_RUN2,0,0}, // S_BOSS_RUN1 + {SPR_BOSS,0,3,A_Chase,S_BOSS_RUN3,0,0}, // S_BOSS_RUN2 + {SPR_BOSS,1,3,A_Chase,S_BOSS_RUN4,0,0}, // S_BOSS_RUN3 + {SPR_BOSS,1,3,A_Chase,S_BOSS_RUN5,0,0}, // S_BOSS_RUN4 + {SPR_BOSS,2,3,A_Chase,S_BOSS_RUN6,0,0}, // S_BOSS_RUN5 + {SPR_BOSS,2,3,A_Chase,S_BOSS_RUN7,0,0}, // S_BOSS_RUN6 + {SPR_BOSS,3,3,A_Chase,S_BOSS_RUN8,0,0}, // S_BOSS_RUN7 + {SPR_BOSS,3,3,A_Chase,S_BOSS_RUN1,0,0}, // S_BOSS_RUN8 + {SPR_BOSS,4,8,A_FaceTarget,S_BOSS_ATK2,0,0}, // S_BOSS_ATK1 + {SPR_BOSS,5,8,A_FaceTarget,S_BOSS_ATK3,0,0}, // S_BOSS_ATK2 + {SPR_BOSS,6,8,A_BruisAttack,S_BOSS_RUN1,0,0}, // S_BOSS_ATK3 + {SPR_BOSS,7,2,NULL,S_BOSS_PAIN2,0,0}, // S_BOSS_PAIN + {SPR_BOSS,7,2,A_Pain,S_BOSS_RUN1,0,0}, // S_BOSS_PAIN2 + {SPR_BOSS,8,8,NULL,S_BOSS_DIE2,0,0}, // S_BOSS_DIE1 + {SPR_BOSS,9,8,A_Scream,S_BOSS_DIE3,0,0}, // S_BOSS_DIE2 + {SPR_BOSS,10,8,NULL,S_BOSS_DIE4,0,0}, // S_BOSS_DIE3 + {SPR_BOSS,11,8,A_Fall,S_BOSS_DIE5,0,0}, // S_BOSS_DIE4 + {SPR_BOSS,12,8,NULL,S_BOSS_DIE6,0,0}, // S_BOSS_DIE5 + {SPR_BOSS,13,8,NULL,S_BOSS_DIE7,0,0}, // S_BOSS_DIE6 + {SPR_BOSS,14,-1,A_BossDeath,S_NULL,0,0}, // S_BOSS_DIE7 + {SPR_BOSS,14,8,NULL,S_BOSS_RAISE2,0,0}, // S_BOSS_RAISE1 + {SPR_BOSS,13,8,NULL,S_BOSS_RAISE3,0,0}, // S_BOSS_RAISE2 + {SPR_BOSS,12,8,NULL,S_BOSS_RAISE4,0,0}, // S_BOSS_RAISE3 + {SPR_BOSS,11,8,NULL,S_BOSS_RAISE5,0,0}, // S_BOSS_RAISE4 + {SPR_BOSS,10,8,NULL,S_BOSS_RAISE6,0,0}, // S_BOSS_RAISE5 + {SPR_BOSS,9,8,NULL,S_BOSS_RAISE7,0,0}, // S_BOSS_RAISE6 + {SPR_BOSS,8,8,NULL,S_BOSS_RUN1,0,0}, // S_BOSS_RAISE7 + {SPR_BOS2,0,10,A_Look,S_BOS2_STND2,0,0}, // S_BOS2_STND + {SPR_BOS2,1,10,A_Look,S_BOS2_STND,0,0}, // S_BOS2_STND2 + {SPR_BOS2,0,3,A_Chase,S_BOS2_RUN2,0,0}, // S_BOS2_RUN1 + {SPR_BOS2,0,3,A_Chase,S_BOS2_RUN3,0,0}, // S_BOS2_RUN2 + {SPR_BOS2,1,3,A_Chase,S_BOS2_RUN4,0,0}, // S_BOS2_RUN3 + {SPR_BOS2,1,3,A_Chase,S_BOS2_RUN5,0,0}, // S_BOS2_RUN4 + {SPR_BOS2,2,3,A_Chase,S_BOS2_RUN6,0,0}, // S_BOS2_RUN5 + {SPR_BOS2,2,3,A_Chase,S_BOS2_RUN7,0,0}, // S_BOS2_RUN6 + {SPR_BOS2,3,3,A_Chase,S_BOS2_RUN8,0,0}, // S_BOS2_RUN7 + {SPR_BOS2,3,3,A_Chase,S_BOS2_RUN1,0,0}, // S_BOS2_RUN8 + {SPR_BOS2,4,8,A_FaceTarget,S_BOS2_ATK2,0,0}, // S_BOS2_ATK1 + {SPR_BOS2,5,8,A_FaceTarget,S_BOS2_ATK3,0,0}, // S_BOS2_ATK2 + {SPR_BOS2,6,8,A_BruisAttack,S_BOS2_RUN1,0,0}, // S_BOS2_ATK3 + {SPR_BOS2,7,2,NULL,S_BOS2_PAIN2,0,0}, // S_BOS2_PAIN + {SPR_BOS2,7,2,A_Pain,S_BOS2_RUN1,0,0}, // S_BOS2_PAIN2 + {SPR_BOS2,8,8,NULL,S_BOS2_DIE2,0,0}, // S_BOS2_DIE1 + {SPR_BOS2,9,8,A_Scream,S_BOS2_DIE3,0,0}, // S_BOS2_DIE2 + {SPR_BOS2,10,8,NULL,S_BOS2_DIE4,0,0}, // S_BOS2_DIE3 + {SPR_BOS2,11,8,A_Fall,S_BOS2_DIE5,0,0}, // S_BOS2_DIE4 + {SPR_BOS2,12,8,NULL,S_BOS2_DIE6,0,0}, // S_BOS2_DIE5 + {SPR_BOS2,13,8,NULL,S_BOS2_DIE7,0,0}, // S_BOS2_DIE6 + {SPR_BOS2,14,-1,NULL,S_NULL,0,0}, // S_BOS2_DIE7 + {SPR_BOS2,14,8,NULL,S_BOS2_RAISE2,0,0}, // S_BOS2_RAISE1 + {SPR_BOS2,13,8,NULL,S_BOS2_RAISE3,0,0}, // S_BOS2_RAISE2 + {SPR_BOS2,12,8,NULL,S_BOS2_RAISE4,0,0}, // S_BOS2_RAISE3 + {SPR_BOS2,11,8,NULL,S_BOS2_RAISE5,0,0}, // S_BOS2_RAISE4 + {SPR_BOS2,10,8,NULL,S_BOS2_RAISE6,0,0}, // S_BOS2_RAISE5 + {SPR_BOS2,9,8,NULL,S_BOS2_RAISE7,0,0}, // S_BOS2_RAISE6 + {SPR_BOS2,8,8,NULL,S_BOS2_RUN1,0,0}, // S_BOS2_RAISE7 + {SPR_SKUL,32768,10,A_Look,S_SKULL_STND2,0,0}, // S_SKULL_STND + {SPR_SKUL,32769,10,A_Look,S_SKULL_STND,0,0}, // S_SKULL_STND2 + {SPR_SKUL,32768,6,A_Chase,S_SKULL_RUN2,0,0}, // S_SKULL_RUN1 + {SPR_SKUL,32769,6,A_Chase,S_SKULL_RUN1,0,0}, // S_SKULL_RUN2 + {SPR_SKUL,32770,10,A_FaceTarget,S_SKULL_ATK2,0,0}, // S_SKULL_ATK1 + {SPR_SKUL,32771,4,A_SkullAttack,S_SKULL_ATK3,0,0}, // S_SKULL_ATK2 + {SPR_SKUL,32770,4,NULL,S_SKULL_ATK4,0,0}, // S_SKULL_ATK3 + {SPR_SKUL,32771,4,NULL,S_SKULL_ATK3,0,0}, // S_SKULL_ATK4 + {SPR_SKUL,32772,3,NULL,S_SKULL_PAIN2,0,0}, // S_SKULL_PAIN + {SPR_SKUL,32772,3,A_Pain,S_SKULL_RUN1,0,0}, // S_SKULL_PAIN2 + {SPR_SKUL,32773,6,NULL,S_SKULL_DIE2,0,0}, // S_SKULL_DIE1 + {SPR_SKUL,32774,6,A_Scream,S_SKULL_DIE3,0,0}, // S_SKULL_DIE2 + {SPR_SKUL,32775,6,NULL,S_SKULL_DIE4,0,0}, // S_SKULL_DIE3 + {SPR_SKUL,32776,6,A_Fall,S_SKULL_DIE5,0,0}, // S_SKULL_DIE4 + {SPR_SKUL,9,6,NULL,S_SKULL_DIE6,0,0}, // S_SKULL_DIE5 + {SPR_SKUL,10,6,NULL,S_NULL,0,0}, // S_SKULL_DIE6 + {SPR_SPID,0,10,A_Look,S_SPID_STND2,0,0}, // S_SPID_STND + {SPR_SPID,1,10,A_Look,S_SPID_STND,0,0}, // S_SPID_STND2 + {SPR_SPID,0,3,A_Metal,S_SPID_RUN2,0,0}, // S_SPID_RUN1 + {SPR_SPID,0,3,A_Chase,S_SPID_RUN3,0,0}, // S_SPID_RUN2 + {SPR_SPID,1,3,A_Chase,S_SPID_RUN4,0,0}, // S_SPID_RUN3 + {SPR_SPID,1,3,A_Chase,S_SPID_RUN5,0,0}, // S_SPID_RUN4 + {SPR_SPID,2,3,A_Metal,S_SPID_RUN6,0,0}, // S_SPID_RUN5 + {SPR_SPID,2,3,A_Chase,S_SPID_RUN7,0,0}, // S_SPID_RUN6 + {SPR_SPID,3,3,A_Chase,S_SPID_RUN8,0,0}, // S_SPID_RUN7 + {SPR_SPID,3,3,A_Chase,S_SPID_RUN9,0,0}, // S_SPID_RUN8 + {SPR_SPID,4,3,A_Metal,S_SPID_RUN10,0,0}, // S_SPID_RUN9 + {SPR_SPID,4,3,A_Chase,S_SPID_RUN11,0,0}, // S_SPID_RUN10 + {SPR_SPID,5,3,A_Chase,S_SPID_RUN12,0,0}, // S_SPID_RUN11 + {SPR_SPID,5,3,A_Chase,S_SPID_RUN1,0,0}, // S_SPID_RUN12 + {SPR_SPID,32768,20,A_FaceTarget,S_SPID_ATK2,0,0}, // S_SPID_ATK1 + {SPR_SPID,32774,4,A_SPosAttack,S_SPID_ATK3,0,0}, // S_SPID_ATK2 + {SPR_SPID,32775,4,A_SPosAttack,S_SPID_ATK4,0,0}, // S_SPID_ATK3 + {SPR_SPID,32775,1,A_SpidRefire,S_SPID_ATK2,0,0}, // S_SPID_ATK4 + {SPR_SPID,8,3,NULL,S_SPID_PAIN2,0,0}, // S_SPID_PAIN + {SPR_SPID,8,3,A_Pain,S_SPID_RUN1,0,0}, // S_SPID_PAIN2 + {SPR_SPID,9,20,A_Scream,S_SPID_DIE2,0,0}, // S_SPID_DIE1 + {SPR_SPID,10,10,A_Fall,S_SPID_DIE3,0,0}, // S_SPID_DIE2 + {SPR_SPID,11,10,NULL,S_SPID_DIE4,0,0}, // S_SPID_DIE3 + {SPR_SPID,12,10,NULL,S_SPID_DIE5,0,0}, // S_SPID_DIE4 + {SPR_SPID,13,10,NULL,S_SPID_DIE6,0,0}, // S_SPID_DIE5 + {SPR_SPID,14,10,NULL,S_SPID_DIE7,0,0}, // S_SPID_DIE6 + {SPR_SPID,15,10,NULL,S_SPID_DIE8,0,0}, // S_SPID_DIE7 + {SPR_SPID,16,10,NULL,S_SPID_DIE9,0,0}, // S_SPID_DIE8 + {SPR_SPID,17,10,NULL,S_SPID_DIE10,0,0}, // S_SPID_DIE9 + {SPR_SPID,18,30,NULL,S_SPID_DIE11,0,0}, // S_SPID_DIE10 + {SPR_SPID,18,-1,A_BossDeath,S_NULL,0,0}, // S_SPID_DIE11 + {SPR_BSPI,0,10,A_Look,S_BSPI_STND2,0,0}, // S_BSPI_STND + {SPR_BSPI,1,10,A_Look,S_BSPI_STND,0,0}, // S_BSPI_STND2 + {SPR_BSPI,0,20,NULL,S_BSPI_RUN1,0,0}, // S_BSPI_SIGHT + {SPR_BSPI,0,3,A_BabyMetal,S_BSPI_RUN2,0,0}, // S_BSPI_RUN1 + {SPR_BSPI,0,3,A_Chase,S_BSPI_RUN3,0,0}, // S_BSPI_RUN2 + {SPR_BSPI,1,3,A_Chase,S_BSPI_RUN4,0,0}, // S_BSPI_RUN3 + {SPR_BSPI,1,3,A_Chase,S_BSPI_RUN5,0,0}, // S_BSPI_RUN4 + {SPR_BSPI,2,3,A_Chase,S_BSPI_RUN6,0,0}, // S_BSPI_RUN5 + {SPR_BSPI,2,3,A_Chase,S_BSPI_RUN7,0,0}, // S_BSPI_RUN6 + {SPR_BSPI,3,3,A_BabyMetal,S_BSPI_RUN8,0,0}, // S_BSPI_RUN7 + {SPR_BSPI,3,3,A_Chase,S_BSPI_RUN9,0,0}, // S_BSPI_RUN8 + {SPR_BSPI,4,3,A_Chase,S_BSPI_RUN10,0,0}, // S_BSPI_RUN9 + {SPR_BSPI,4,3,A_Chase,S_BSPI_RUN11,0,0}, // S_BSPI_RUN10 + {SPR_BSPI,5,3,A_Chase,S_BSPI_RUN12,0,0}, // S_BSPI_RUN11 + {SPR_BSPI,5,3,A_Chase,S_BSPI_RUN1,0,0}, // S_BSPI_RUN12 + {SPR_BSPI,32768,20,A_FaceTarget,S_BSPI_ATK2,0,0}, // S_BSPI_ATK1 + {SPR_BSPI,32774,4,A_BspiAttack,S_BSPI_ATK3,0,0}, // S_BSPI_ATK2 + {SPR_BSPI,32775,4,NULL,S_BSPI_ATK4,0,0}, // S_BSPI_ATK3 + {SPR_BSPI,32775,1,A_SpidRefire,S_BSPI_ATK2,0,0}, // S_BSPI_ATK4 + {SPR_BSPI,8,3,NULL,S_BSPI_PAIN2,0,0}, // S_BSPI_PAIN + {SPR_BSPI,8,3,A_Pain,S_BSPI_RUN1,0,0}, // S_BSPI_PAIN2 + {SPR_BSPI,9,20,A_Scream,S_BSPI_DIE2,0,0}, // S_BSPI_DIE1 + {SPR_BSPI,10,7,A_Fall,S_BSPI_DIE3,0,0}, // S_BSPI_DIE2 + {SPR_BSPI,11,7,NULL,S_BSPI_DIE4,0,0}, // S_BSPI_DIE3 + {SPR_BSPI,12,7,NULL,S_BSPI_DIE5,0,0}, // S_BSPI_DIE4 + {SPR_BSPI,13,7,NULL,S_BSPI_DIE6,0,0}, // S_BSPI_DIE5 + {SPR_BSPI,14,7,NULL,S_BSPI_DIE7,0,0}, // S_BSPI_DIE6 + {SPR_BSPI,15,-1,A_BossDeath,S_NULL,0,0}, // S_BSPI_DIE7 + {SPR_BSPI,15,5,NULL,S_BSPI_RAISE2,0,0}, // S_BSPI_RAISE1 + {SPR_BSPI,14,5,NULL,S_BSPI_RAISE3,0,0}, // S_BSPI_RAISE2 + {SPR_BSPI,13,5,NULL,S_BSPI_RAISE4,0,0}, // S_BSPI_RAISE3 + {SPR_BSPI,12,5,NULL,S_BSPI_RAISE5,0,0}, // S_BSPI_RAISE4 + {SPR_BSPI,11,5,NULL,S_BSPI_RAISE6,0,0}, // S_BSPI_RAISE5 + {SPR_BSPI,10,5,NULL,S_BSPI_RAISE7,0,0}, // S_BSPI_RAISE6 + {SPR_BSPI,9,5,NULL,S_BSPI_RUN1,0,0}, // S_BSPI_RAISE7 + {SPR_APLS,32768,5,NULL,S_ARACH_PLAZ2,0,0}, // S_ARACH_PLAZ + {SPR_APLS,32769,5,NULL,S_ARACH_PLAZ,0,0}, // S_ARACH_PLAZ2 + {SPR_APBX,32768,5,NULL,S_ARACH_PLEX2,0,0}, // S_ARACH_PLEX + {SPR_APBX,32769,5,NULL,S_ARACH_PLEX3,0,0}, // S_ARACH_PLEX2 + {SPR_APBX,32770,5,NULL,S_ARACH_PLEX4,0,0}, // S_ARACH_PLEX3 + {SPR_APBX,32771,5,NULL,S_ARACH_PLEX5,0,0}, // S_ARACH_PLEX4 + {SPR_APBX,32772,5,NULL,S_NULL,0,0}, // S_ARACH_PLEX5 + {SPR_CYBR,0,10,A_Look,S_CYBER_STND2,0,0}, // S_CYBER_STND + {SPR_CYBR,1,10,A_Look,S_CYBER_STND,0,0}, // S_CYBER_STND2 + {SPR_CYBR,0,3,A_Hoof,S_CYBER_RUN2,0,0}, // S_CYBER_RUN1 + {SPR_CYBR,0,3,A_Chase,S_CYBER_RUN3,0,0}, // S_CYBER_RUN2 + {SPR_CYBR,1,3,A_Chase,S_CYBER_RUN4,0,0}, // S_CYBER_RUN3 + {SPR_CYBR,1,3,A_Chase,S_CYBER_RUN5,0,0}, // S_CYBER_RUN4 + {SPR_CYBR,2,3,A_Chase,S_CYBER_RUN6,0,0}, // S_CYBER_RUN5 + {SPR_CYBR,2,3,A_Chase,S_CYBER_RUN7,0,0}, // S_CYBER_RUN6 + {SPR_CYBR,3,3,A_Metal,S_CYBER_RUN8,0,0}, // S_CYBER_RUN7 + {SPR_CYBR,3,3,A_Chase,S_CYBER_RUN1,0,0}, // S_CYBER_RUN8 + {SPR_CYBR,4,6,A_FaceTarget,S_CYBER_ATK2,0,0}, // S_CYBER_ATK1 + {SPR_CYBR,5,12,A_CyberAttack,S_CYBER_ATK3,0,0}, // S_CYBER_ATK2 + {SPR_CYBR,4,12,A_FaceTarget,S_CYBER_ATK4,0,0}, // S_CYBER_ATK3 + {SPR_CYBR,5,12,A_CyberAttack,S_CYBER_ATK5,0,0}, // S_CYBER_ATK4 + {SPR_CYBR,4,12,A_FaceTarget,S_CYBER_ATK6,0,0}, // S_CYBER_ATK5 + {SPR_CYBR,5,12,A_CyberAttack,S_CYBER_RUN1,0,0}, // S_CYBER_ATK6 + {SPR_CYBR,6,10,A_Pain,S_CYBER_RUN1,0,0}, // S_CYBER_PAIN + {SPR_CYBR,7,10,NULL,S_CYBER_DIE2,0,0}, // S_CYBER_DIE1 + {SPR_CYBR,8,10,A_Scream,S_CYBER_DIE3,0,0}, // S_CYBER_DIE2 + {SPR_CYBR,9,10,NULL,S_CYBER_DIE4,0,0}, // S_CYBER_DIE3 + {SPR_CYBR,10,10,NULL,S_CYBER_DIE5,0,0}, // S_CYBER_DIE4 + {SPR_CYBR,11,10,NULL,S_CYBER_DIE6,0,0}, // S_CYBER_DIE5 + {SPR_CYBR,12,10,A_Fall,S_CYBER_DIE7,0,0}, // S_CYBER_DIE6 + {SPR_CYBR,13,10,NULL,S_CYBER_DIE8,0,0}, // S_CYBER_DIE7 + {SPR_CYBR,14,10,NULL,S_CYBER_DIE9,0,0}, // S_CYBER_DIE8 + {SPR_CYBR,15,30,NULL,S_CYBER_DIE10,0,0}, // S_CYBER_DIE9 + {SPR_CYBR,15,-1,A_BossDeath,S_NULL,0,0}, // S_CYBER_DIE10 + {SPR_PAIN,0,10,A_Look,S_PAIN_STND,0,0}, // S_PAIN_STND + {SPR_PAIN,0,3,A_Chase,S_PAIN_RUN2,0,0}, // S_PAIN_RUN1 + {SPR_PAIN,0,3,A_Chase,S_PAIN_RUN3,0,0}, // S_PAIN_RUN2 + {SPR_PAIN,1,3,A_Chase,S_PAIN_RUN4,0,0}, // S_PAIN_RUN3 + {SPR_PAIN,1,3,A_Chase,S_PAIN_RUN5,0,0}, // S_PAIN_RUN4 + {SPR_PAIN,2,3,A_Chase,S_PAIN_RUN6,0,0}, // S_PAIN_RUN5 + {SPR_PAIN,2,3,A_Chase,S_PAIN_RUN1,0,0}, // S_PAIN_RUN6 + {SPR_PAIN,3,5,A_FaceTarget,S_PAIN_ATK2,0,0}, // S_PAIN_ATK1 + {SPR_PAIN,4,5,A_FaceTarget,S_PAIN_ATK3,0,0}, // S_PAIN_ATK2 + {SPR_PAIN,32773,5,A_FaceTarget,S_PAIN_ATK4,0,0}, // S_PAIN_ATK3 + {SPR_PAIN,32773,0,A_PainAttack,S_PAIN_RUN1,0,0}, // S_PAIN_ATK4 + {SPR_PAIN,6,6,NULL,S_PAIN_PAIN2,0,0}, // S_PAIN_PAIN + {SPR_PAIN,6,6,A_Pain,S_PAIN_RUN1,0,0}, // S_PAIN_PAIN2 + {SPR_PAIN,32775,8,NULL,S_PAIN_DIE2,0,0}, // S_PAIN_DIE1 + {SPR_PAIN,32776,8,A_Scream,S_PAIN_DIE3,0,0}, // S_PAIN_DIE2 + {SPR_PAIN,32777,8,NULL,S_PAIN_DIE4,0,0}, // S_PAIN_DIE3 + {SPR_PAIN,32778,8,NULL,S_PAIN_DIE5,0,0}, // S_PAIN_DIE4 + {SPR_PAIN,32779,8,A_PainDie,S_PAIN_DIE6,0,0}, // S_PAIN_DIE5 + {SPR_PAIN,32780,8,NULL,S_NULL,0,0}, // S_PAIN_DIE6 + {SPR_PAIN,12,8,NULL,S_PAIN_RAISE2,0,0}, // S_PAIN_RAISE1 + {SPR_PAIN,11,8,NULL,S_PAIN_RAISE3,0,0}, // S_PAIN_RAISE2 + {SPR_PAIN,10,8,NULL,S_PAIN_RAISE4,0,0}, // S_PAIN_RAISE3 + {SPR_PAIN,9,8,NULL,S_PAIN_RAISE5,0,0}, // S_PAIN_RAISE4 + {SPR_PAIN,8,8,NULL,S_PAIN_RAISE6,0,0}, // S_PAIN_RAISE5 + {SPR_PAIN,7,8,NULL,S_PAIN_RUN1,0,0}, // S_PAIN_RAISE6 + {SPR_SSWV,0,10,A_Look,S_SSWV_STND2,0,0}, // S_SSWV_STND + {SPR_SSWV,1,10,A_Look,S_SSWV_STND,0,0}, // S_SSWV_STND2 + {SPR_SSWV,0,3,A_Chase,S_SSWV_RUN2,0,0}, // S_SSWV_RUN1 + {SPR_SSWV,0,3,A_Chase,S_SSWV_RUN3,0,0}, // S_SSWV_RUN2 + {SPR_SSWV,1,3,A_Chase,S_SSWV_RUN4,0,0}, // S_SSWV_RUN3 + {SPR_SSWV,1,3,A_Chase,S_SSWV_RUN5,0,0}, // S_SSWV_RUN4 + {SPR_SSWV,2,3,A_Chase,S_SSWV_RUN6,0,0}, // S_SSWV_RUN5 + {SPR_SSWV,2,3,A_Chase,S_SSWV_RUN7,0,0}, // S_SSWV_RUN6 + {SPR_SSWV,3,3,A_Chase,S_SSWV_RUN8,0,0}, // S_SSWV_RUN7 + {SPR_SSWV,3,3,A_Chase,S_SSWV_RUN1,0,0}, // S_SSWV_RUN8 + {SPR_SSWV,4,10,A_FaceTarget,S_SSWV_ATK2,0,0}, // S_SSWV_ATK1 + {SPR_SSWV,5,10,A_FaceTarget,S_SSWV_ATK3,0,0}, // S_SSWV_ATK2 + {SPR_SSWV,32774,4,A_CPosAttack,S_SSWV_ATK4,0,0}, // S_SSWV_ATK3 + {SPR_SSWV,5,6,A_FaceTarget,S_SSWV_ATK5,0,0}, // S_SSWV_ATK4 + {SPR_SSWV,32774,4,A_CPosAttack,S_SSWV_ATK6,0,0}, // S_SSWV_ATK5 + {SPR_SSWV,5,1,A_CPosRefire,S_SSWV_ATK2,0,0}, // S_SSWV_ATK6 + {SPR_SSWV,7,3,NULL,S_SSWV_PAIN2,0,0}, // S_SSWV_PAIN + {SPR_SSWV,7,3,A_Pain,S_SSWV_RUN1,0,0}, // S_SSWV_PAIN2 + {SPR_SSWV,8,5,NULL,S_SSWV_DIE2,0,0}, // S_SSWV_DIE1 + {SPR_SSWV,9,5,A_Scream,S_SSWV_DIE3,0,0}, // S_SSWV_DIE2 + {SPR_SSWV,10,5,A_Fall,S_SSWV_DIE4,0,0}, // S_SSWV_DIE3 + {SPR_SSWV,11,5,NULL,S_SSWV_DIE5,0,0}, // S_SSWV_DIE4 + {SPR_SSWV,12,-1,NULL,S_NULL,0,0}, // S_SSWV_DIE5 + {SPR_SSWV,13,5,NULL,S_SSWV_XDIE2,0,0}, // S_SSWV_XDIE1 + {SPR_SSWV,14,5,A_XScream,S_SSWV_XDIE3,0,0}, // S_SSWV_XDIE2 + {SPR_SSWV,15,5,A_Fall,S_SSWV_XDIE4,0,0}, // S_SSWV_XDIE3 + {SPR_SSWV,16,5,NULL,S_SSWV_XDIE5,0,0}, // S_SSWV_XDIE4 + {SPR_SSWV,17,5,NULL,S_SSWV_XDIE6,0,0}, // S_SSWV_XDIE5 + {SPR_SSWV,18,5,NULL,S_SSWV_XDIE7,0,0}, // S_SSWV_XDIE6 + {SPR_SSWV,19,5,NULL,S_SSWV_XDIE8,0,0}, // S_SSWV_XDIE7 + {SPR_SSWV,20,5,NULL,S_SSWV_XDIE9,0,0}, // S_SSWV_XDIE8 + {SPR_SSWV,21,-1,NULL,S_NULL,0,0}, // S_SSWV_XDIE9 + {SPR_SSWV,12,5,NULL,S_SSWV_RAISE2,0,0}, // S_SSWV_RAISE1 + {SPR_SSWV,11,5,NULL,S_SSWV_RAISE3,0,0}, // S_SSWV_RAISE2 + {SPR_SSWV,10,5,NULL,S_SSWV_RAISE4,0,0}, // S_SSWV_RAISE3 + {SPR_SSWV,9,5,NULL,S_SSWV_RAISE5,0,0}, // S_SSWV_RAISE4 + {SPR_SSWV,8,5,NULL,S_SSWV_RUN1,0,0}, // S_SSWV_RAISE5 + {SPR_KEEN,0,-1,NULL,S_KEENSTND,0,0}, // S_KEENSTND + {SPR_KEEN,0,6,NULL,S_COMMKEEN2,0,0}, // S_COMMKEEN + {SPR_KEEN,1,6,NULL,S_COMMKEEN3,0,0}, // S_COMMKEEN2 + {SPR_KEEN,2,6,A_Scream,S_COMMKEEN4,0,0}, // S_COMMKEEN3 + {SPR_KEEN,3,6,NULL,S_COMMKEEN5,0,0}, // S_COMMKEEN4 + {SPR_KEEN,4,6,NULL,S_COMMKEEN6,0,0}, // S_COMMKEEN5 + {SPR_KEEN,5,6,NULL,S_COMMKEEN7,0,0}, // S_COMMKEEN6 + {SPR_KEEN,6,6,NULL,S_COMMKEEN8,0,0}, // S_COMMKEEN7 + {SPR_KEEN,7,6,NULL,S_COMMKEEN9,0,0}, // S_COMMKEEN8 + {SPR_KEEN,8,6,NULL,S_COMMKEEN10,0,0}, // S_COMMKEEN9 + {SPR_KEEN,9,6,NULL,S_COMMKEEN11,0,0}, // S_COMMKEEN10 + {SPR_KEEN,10,6,A_KeenDie,S_COMMKEEN12,0,0},// S_COMMKEEN11 + {SPR_KEEN,11,-1,NULL,S_NULL,0,0}, // S_COMMKEEN12 + {SPR_KEEN,12,4,NULL,S_KEENPAIN2,0,0}, // S_KEENPAIN + {SPR_KEEN,12,8,A_Pain,S_KEENSTND,0,0}, // S_KEENPAIN2 + {SPR_BBRN,0,-1,NULL,S_NULL,0,0}, // S_BRAIN + {SPR_BBRN,1,36,A_BrainPain,S_BRAIN,0,0}, // S_BRAIN_PAIN + {SPR_BBRN,0,100,A_BrainScream,S_BRAIN_DIE2,0,0}, // S_BRAIN_DIE1 + {SPR_BBRN,0,10,NULL,S_BRAIN_DIE3,0,0}, // S_BRAIN_DIE2 + {SPR_BBRN,0,10,NULL,S_BRAIN_DIE4,0,0}, // S_BRAIN_DIE3 + {SPR_BBRN,0,-1,A_BrainDie,S_NULL,0,0}, // S_BRAIN_DIE4 + {SPR_SSWV,0,10,A_Look,S_BRAINEYE,0,0}, // S_BRAINEYE + {SPR_SSWV,0,181,A_BrainAwake,S_BRAINEYE1,0,0}, // S_BRAINEYESEE + {SPR_SSWV,0,150,A_BrainSpit,S_BRAINEYE1,0,0}, // S_BRAINEYE1 + {SPR_BOSF,32768,3,A_SpawnSound,S_SPAWN2,0,0}, // S_SPAWN1 + {SPR_BOSF,32769,3,A_SpawnFly,S_SPAWN3,0,0}, // S_SPAWN2 + {SPR_BOSF,32770,3,A_SpawnFly,S_SPAWN4,0,0}, // S_SPAWN3 + {SPR_BOSF,32771,3,A_SpawnFly,S_SPAWN1,0,0}, // S_SPAWN4 + {SPR_FIRE,32768,4,A_Fire,S_SPAWNFIRE2,0,0}, // S_SPAWNFIRE1 + {SPR_FIRE,32769,4,A_Fire,S_SPAWNFIRE3,0,0}, // S_SPAWNFIRE2 + {SPR_FIRE,32770,4,A_Fire,S_SPAWNFIRE4,0,0}, // S_SPAWNFIRE3 + {SPR_FIRE,32771,4,A_Fire,S_SPAWNFIRE5,0,0}, // S_SPAWNFIRE4 + {SPR_FIRE,32772,4,A_Fire,S_SPAWNFIRE6,0,0}, // S_SPAWNFIRE5 + {SPR_FIRE,32773,4,A_Fire,S_SPAWNFIRE7,0,0}, // S_SPAWNFIRE6 + {SPR_FIRE,32774,4,A_Fire,S_SPAWNFIRE8,0,0}, // S_SPAWNFIRE7 + {SPR_FIRE,32775,4,A_Fire,S_NULL,0,0}, // S_SPAWNFIRE8 + {SPR_MISL,32769,10,NULL,S_BRAINEXPLODE2,0,0}, // S_BRAINEXPLODE1 + {SPR_MISL,32770,10,NULL,S_BRAINEXPLODE3,0,0}, // S_BRAINEXPLODE2 + {SPR_MISL,32771,10,A_BrainExplode,S_NULL,0,0}, // S_BRAINEXPLODE3 + {SPR_ARM1,0,6,NULL,S_ARM1A,0,0}, // S_ARM1 + {SPR_ARM1,32769,7,NULL,S_ARM1,0,0}, // S_ARM1A + {SPR_ARM2,0,6,NULL,S_ARM2A,0,0}, // S_ARM2 + {SPR_ARM2,32769,6,NULL,S_ARM2,0,0}, // S_ARM2A + {SPR_BAR1,0,6,NULL,S_BAR2,0,0}, // S_BAR1 + {SPR_BAR1,1,6,NULL,S_BAR1,0,0}, // S_BAR2 + {SPR_BEXP,32768,5,NULL,S_BEXP2,0,0}, // S_BEXP + {SPR_BEXP,32769,5,A_Scream,S_BEXP3,0,0}, // S_BEXP2 + {SPR_BEXP,32770,5,NULL,S_BEXP4,0,0}, // S_BEXP3 + {SPR_BEXP,32771,10,A_Explode,S_BEXP5,0,0}, // S_BEXP4 + {SPR_BEXP,32772,10,NULL,S_NULL,0,0}, // S_BEXP5 + {SPR_FCAN,32768,4,NULL,S_BBAR2,0,0}, // S_BBAR1 + {SPR_FCAN,32769,4,NULL,S_BBAR3,0,0}, // S_BBAR2 + {SPR_FCAN,32770,4,NULL,S_BBAR1,0,0}, // S_BBAR3 + {SPR_BON1,0,6,NULL,S_BON1A,0,0}, // S_BON1 + {SPR_BON1,1,6,NULL,S_BON1B,0,0}, // S_BON1A + {SPR_BON1,2,6,NULL,S_BON1C,0,0}, // S_BON1B + {SPR_BON1,3,6,NULL,S_BON1D,0,0}, // S_BON1C + {SPR_BON1,2,6,NULL,S_BON1E,0,0}, // S_BON1D + {SPR_BON1,1,6,NULL,S_BON1,0,0}, // S_BON1E + {SPR_BON2,0,6,NULL,S_BON2A,0,0}, // S_BON2 + {SPR_BON2,1,6,NULL,S_BON2B,0,0}, // S_BON2A + {SPR_BON2,2,6,NULL,S_BON2C,0,0}, // S_BON2B + {SPR_BON2,3,6,NULL,S_BON2D,0,0}, // S_BON2C + {SPR_BON2,2,6,NULL,S_BON2E,0,0}, // S_BON2D + {SPR_BON2,1,6,NULL,S_BON2,0,0}, // S_BON2E + {SPR_BKEY,0,10,NULL,S_BKEY2,0,0}, // S_BKEY + {SPR_BKEY,32769,10,NULL,S_BKEY,0,0}, // S_BKEY2 + {SPR_RKEY,0,10,NULL,S_RKEY2,0,0}, // S_RKEY + {SPR_RKEY,32769,10,NULL,S_RKEY,0,0}, // S_RKEY2 + {SPR_YKEY,0,10,NULL,S_YKEY2,0,0}, // S_YKEY + {SPR_YKEY,32769,10,NULL,S_YKEY,0,0}, // S_YKEY2 + {SPR_BSKU,0,10,NULL,S_BSKULL2,0,0}, // S_BSKULL + {SPR_BSKU,32769,10,NULL,S_BSKULL,0,0}, // S_BSKULL2 + {SPR_RSKU,0,10,NULL,S_RSKULL2,0,0}, // S_RSKULL + {SPR_RSKU,32769,10,NULL,S_RSKULL,0,0}, // S_RSKULL2 + {SPR_YSKU,0,10,NULL,S_YSKULL2,0,0}, // S_YSKULL + {SPR_YSKU,32769,10,NULL,S_YSKULL,0,0}, // S_YSKULL2 + {SPR_STIM,0,-1,NULL,S_NULL,0,0}, // S_STIM + {SPR_MEDI,0,-1,NULL,S_NULL,0,0}, // S_MEDI + {SPR_SOUL,32768,6,NULL,S_SOUL2,0,0}, // S_SOUL + {SPR_SOUL,32769,6,NULL,S_SOUL3,0,0}, // S_SOUL2 + {SPR_SOUL,32770,6,NULL,S_SOUL4,0,0}, // S_SOUL3 + {SPR_SOUL,32771,6,NULL,S_SOUL5,0,0}, // S_SOUL4 + {SPR_SOUL,32770,6,NULL,S_SOUL6,0,0}, // S_SOUL5 + {SPR_SOUL,32769,6,NULL,S_SOUL,0,0}, // S_SOUL6 + {SPR_PINV,32768,6,NULL,S_PINV2,0,0}, // S_PINV + {SPR_PINV,32769,6,NULL,S_PINV3,0,0}, // S_PINV2 + {SPR_PINV,32770,6,NULL,S_PINV4,0,0}, // S_PINV3 + {SPR_PINV,32771,6,NULL,S_PINV,0,0}, // S_PINV4 + {SPR_PSTR,32768,-1,NULL,S_NULL,0,0}, // S_PSTR + {SPR_PINS,32768,6,NULL,S_PINS2,0,0}, // S_PINS + {SPR_PINS,32769,6,NULL,S_PINS3,0,0}, // S_PINS2 + {SPR_PINS,32770,6,NULL,S_PINS4,0,0}, // S_PINS3 + {SPR_PINS,32771,6,NULL,S_PINS,0,0}, // S_PINS4 + {SPR_MEGA,32768,6,NULL,S_MEGA2,0,0}, // S_MEGA + {SPR_MEGA,32769,6,NULL,S_MEGA3,0,0}, // S_MEGA2 + {SPR_MEGA,32770,6,NULL,S_MEGA4,0,0}, // S_MEGA3 + {SPR_MEGA,32771,6,NULL,S_MEGA,0,0}, // S_MEGA4 + {SPR_SUIT,32768,-1,NULL,S_NULL,0,0}, // S_SUIT + {SPR_PMAP,32768,6,NULL,S_PMAP2,0,0}, // S_PMAP + {SPR_PMAP,32769,6,NULL,S_PMAP3,0,0}, // S_PMAP2 + {SPR_PMAP,32770,6,NULL,S_PMAP4,0,0}, // S_PMAP3 + {SPR_PMAP,32771,6,NULL,S_PMAP5,0,0}, // S_PMAP4 + {SPR_PMAP,32770,6,NULL,S_PMAP6,0,0}, // S_PMAP5 + {SPR_PMAP,32769,6,NULL,S_PMAP,0,0}, // S_PMAP6 + {SPR_PVIS,32768,6,NULL,S_PVIS2,0,0}, // S_PVIS + {SPR_PVIS,1,6,NULL,S_PVIS,0,0}, // S_PVIS2 + {SPR_CLIP,0,-1,NULL,S_NULL,0,0}, // S_CLIP + {SPR_AMMO,0,-1,NULL,S_NULL,0,0}, // S_AMMO + {SPR_ROCK,0,-1,NULL,S_NULL,0,0}, // S_ROCK + {SPR_BROK,0,-1,NULL,S_NULL,0,0}, // S_BROK + {SPR_CELL,0,-1,NULL,S_NULL,0,0}, // S_CELL + {SPR_CELP,0,-1,NULL,S_NULL,0,0}, // S_CELP + {SPR_SHEL,0,-1,NULL,S_NULL,0,0}, // S_SHEL + {SPR_SBOX,0,-1,NULL,S_NULL,0,0}, // S_SBOX + {SPR_BPAK,0,-1,NULL,S_NULL,0,0}, // S_BPAK + {SPR_BFUG,0,-1,NULL,S_NULL,0,0}, // S_BFUG + {SPR_MGUN,0,-1,NULL,S_NULL,0,0}, // S_MGUN + {SPR_CSAW,0,-1,NULL,S_NULL,0,0}, // S_CSAW + {SPR_LAUN,0,-1,NULL,S_NULL,0,0}, // S_LAUN + {SPR_PLAS,0,-1,NULL,S_NULL,0,0}, // S_PLAS + {SPR_SHOT,0,-1,NULL,S_NULL,0,0}, // S_SHOT + {SPR_SGN2,0,-1,NULL,S_NULL,0,0}, // S_SHOT2 + {SPR_COLU,32768,-1,NULL,S_NULL,0,0}, // S_COLU + {SPR_SMT2,0,-1,NULL,S_NULL,0,0}, // S_STALAG + {SPR_GOR1,0,10,NULL,S_BLOODYTWITCH2,0,0}, // S_BLOODYTWITCH + {SPR_GOR1,1,15,NULL,S_BLOODYTWITCH3,0,0}, // S_BLOODYTWITCH2 + {SPR_GOR1,2,8,NULL,S_BLOODYTWITCH4,0,0}, // S_BLOODYTWITCH3 + {SPR_GOR1,1,6,NULL,S_BLOODYTWITCH,0,0}, // S_BLOODYTWITCH4 + {SPR_PLAY,13,-1,NULL,S_NULL,0,0}, // S_DEADTORSO + {SPR_PLAY,18,-1,NULL,S_NULL,0,0}, // S_DEADBOTTOM + {SPR_POL2,0,-1,NULL,S_NULL,0,0}, // S_HEADSONSTICK + {SPR_POL5,0,-1,NULL,S_NULL,0,0}, // S_GIBS + {SPR_POL4,0,-1,NULL,S_NULL,0,0}, // S_HEADONASTICK + {SPR_POL3,32768,6,NULL,S_HEADCANDLES2,0,0}, // S_HEADCANDLES + {SPR_POL3,32769,6,NULL,S_HEADCANDLES,0,0}, // S_HEADCANDLES2 + {SPR_POL1,0,-1,NULL,S_NULL,0,0}, // S_DEADSTICK + {SPR_POL6,0,6,NULL,S_LIVESTICK2,0,0}, // S_LIVESTICK + {SPR_POL6,1,8,NULL,S_LIVESTICK,0,0}, // S_LIVESTICK2 + {SPR_GOR2,0,-1,NULL,S_NULL,0,0}, // S_MEAT2 + {SPR_GOR3,0,-1,NULL,S_NULL,0,0}, // S_MEAT3 + {SPR_GOR4,0,-1,NULL,S_NULL,0,0}, // S_MEAT4 + {SPR_GOR5,0,-1,NULL,S_NULL,0,0}, // S_MEAT5 + {SPR_SMIT,0,-1,NULL,S_NULL,0,0}, // S_STALAGTITE + {SPR_COL1,0,-1,NULL,S_NULL,0,0}, // S_TALLGRNCOL + {SPR_COL2,0,-1,NULL,S_NULL,0,0}, // S_SHRTGRNCOL + {SPR_COL3,0,-1,NULL,S_NULL,0,0}, // S_TALLREDCOL + {SPR_COL4,0,-1,NULL,S_NULL,0,0}, // S_SHRTREDCOL + {SPR_CAND,32768,-1,NULL,S_NULL,0,0}, // S_CANDLESTIK + {SPR_CBRA,32768,-1,NULL,S_NULL,0,0}, // S_CANDELABRA + {SPR_COL6,0,-1,NULL,S_NULL,0,0}, // S_SKULLCOL + {SPR_TRE1,0,-1,NULL,S_NULL,0,0}, // S_TORCHTREE + {SPR_TRE2,0,-1,NULL,S_NULL,0,0}, // S_BIGTREE + {SPR_ELEC,0,-1,NULL,S_NULL,0,0}, // S_TECHPILLAR + {SPR_CEYE,32768,6,NULL,S_EVILEYE2,0,0}, // S_EVILEYE + {SPR_CEYE,32769,6,NULL,S_EVILEYE3,0,0}, // S_EVILEYE2 + {SPR_CEYE,32770,6,NULL,S_EVILEYE4,0,0}, // S_EVILEYE3 + {SPR_CEYE,32769,6,NULL,S_EVILEYE,0,0}, // S_EVILEYE4 + {SPR_FSKU,32768,6,NULL,S_FLOATSKULL2,0,0}, // S_FLOATSKULL + {SPR_FSKU,32769,6,NULL,S_FLOATSKULL3,0,0}, // S_FLOATSKULL2 + {SPR_FSKU,32770,6,NULL,S_FLOATSKULL,0,0}, // S_FLOATSKULL3 + {SPR_COL5,0,14,NULL,S_HEARTCOL2,0,0}, // S_HEARTCOL + {SPR_COL5,1,14,NULL,S_HEARTCOL,0,0}, // S_HEARTCOL2 + {SPR_TBLU,32768,4,NULL,S_BLUETORCH2,0,0}, // S_BLUETORCH + {SPR_TBLU,32769,4,NULL,S_BLUETORCH3,0,0}, // S_BLUETORCH2 + {SPR_TBLU,32770,4,NULL,S_BLUETORCH4,0,0}, // S_BLUETORCH3 + {SPR_TBLU,32771,4,NULL,S_BLUETORCH,0,0}, // S_BLUETORCH4 + {SPR_TGRN,32768,4,NULL,S_GREENTORCH2,0,0}, // S_GREENTORCH + {SPR_TGRN,32769,4,NULL,S_GREENTORCH3,0,0}, // S_GREENTORCH2 + {SPR_TGRN,32770,4,NULL,S_GREENTORCH4,0,0}, // S_GREENTORCH3 + {SPR_TGRN,32771,4,NULL,S_GREENTORCH,0,0}, // S_GREENTORCH4 + {SPR_TRED,32768,4,NULL,S_REDTORCH2,0,0}, // S_REDTORCH + {SPR_TRED,32769,4,NULL,S_REDTORCH3,0,0}, // S_REDTORCH2 + {SPR_TRED,32770,4,NULL,S_REDTORCH4,0,0}, // S_REDTORCH3 + {SPR_TRED,32771,4,NULL,S_REDTORCH,0,0}, // S_REDTORCH4 + {SPR_SMBT,32768,4,NULL,S_BTORCHSHRT2,0,0}, // S_BTORCHSHRT + {SPR_SMBT,32769,4,NULL,S_BTORCHSHRT3,0,0}, // S_BTORCHSHRT2 + {SPR_SMBT,32770,4,NULL,S_BTORCHSHRT4,0,0}, // S_BTORCHSHRT3 + {SPR_SMBT,32771,4,NULL,S_BTORCHSHRT,0,0}, // S_BTORCHSHRT4 + {SPR_SMGT,32768,4,NULL,S_GTORCHSHRT2,0,0}, // S_GTORCHSHRT + {SPR_SMGT,32769,4,NULL,S_GTORCHSHRT3,0,0}, // S_GTORCHSHRT2 + {SPR_SMGT,32770,4,NULL,S_GTORCHSHRT4,0,0}, // S_GTORCHSHRT3 + {SPR_SMGT,32771,4,NULL,S_GTORCHSHRT,0,0}, // S_GTORCHSHRT4 + {SPR_SMRT,32768,4,NULL,S_RTORCHSHRT2,0,0}, // S_RTORCHSHRT + {SPR_SMRT,32769,4,NULL,S_RTORCHSHRT3,0,0}, // S_RTORCHSHRT2 + {SPR_SMRT,32770,4,NULL,S_RTORCHSHRT4,0,0}, // S_RTORCHSHRT3 + {SPR_SMRT,32771,4,NULL,S_RTORCHSHRT,0,0}, // S_RTORCHSHRT4 + {SPR_HDB1,0,-1,NULL,S_NULL,0,0}, // S_HANGNOGUTS + {SPR_HDB2,0,-1,NULL,S_NULL,0,0}, // S_HANGBNOBRAIN + {SPR_HDB3,0,-1,NULL,S_NULL,0,0}, // S_HANGTLOOKDN + {SPR_HDB4,0,-1,NULL,S_NULL,0,0}, // S_HANGTSKULL + {SPR_HDB5,0,-1,NULL,S_NULL,0,0}, // S_HANGTLOOKUP + {SPR_HDB6,0,-1,NULL,S_NULL,0,0}, // S_HANGTNOBRAIN + {SPR_POB1,0,-1,NULL,S_NULL,0,0}, // S_COLONGIBS + {SPR_POB2,0,-1,NULL,S_NULL,0,0}, // S_SMALLPOOL + {SPR_BRS1,0,-1,NULL,S_NULL,0,0}, // S_BRAINSTEM + {SPR_TLMP,32768,4,NULL,S_TECHLAMP2,0,0}, // S_TECHLAMP + {SPR_TLMP,32769,4,NULL,S_TECHLAMP3,0,0}, // S_TECHLAMP2 + {SPR_TLMP,32770,4,NULL,S_TECHLAMP4,0,0}, // S_TECHLAMP3 + {SPR_TLMP,32771,4,NULL,S_TECHLAMP,0,0}, // S_TECHLAMP4 + {SPR_TLP2,32768,4,NULL,S_TECH2LAMP2,0,0}, // S_TECH2LAMP + {SPR_TLP2,32769,4,NULL,S_TECH2LAMP3,0,0}, // S_TECH2LAMP2 + {SPR_TLP2,32770,4,NULL,S_TECH2LAMP4,0,0}, // S_TECH2LAMP3 + {SPR_TLP2,32771,4,NULL,S_TECH2LAMP,0,0}, // S_TECH2LAMP4 + {SPR_TNT1,0,-1,NULL,S_TNT1,0,0}, // S_TNT1 // phares 3/8/98 + + // killough 8/9/98: grenade + {SPR_MISL,32768,1000,A_Die,S_GRENADE}, // S_GRENADE + + // killough 8/10/98: variable damage explosion + {SPR_MISL,32769,4,A_Scream,S_DETONATE2}, // S_DETONATE + {SPR_MISL,32770,6,A_Detonate,S_DETONATE3}, // S_DETONATE2 + {SPR_MISL,32771,10,NULL,S_NULL}, // S_DETONATE3 + + // killough 7/19/98: Marine's best friend :) + {SPR_DOGS,0,10,A_Look,S_DOGS_STND2}, // S_DOGS_STND + {SPR_DOGS,1,10,A_Look,S_DOGS_STND}, // S_DOGS_STND2 + {SPR_DOGS,0,2,A_Chase,S_DOGS_RUN2}, // S_DOGS_RUN1 + {SPR_DOGS,0,2,A_Chase,S_DOGS_RUN3}, // S_DOGS_RUN2 + {SPR_DOGS,1,2,A_Chase,S_DOGS_RUN4}, // S_DOGS_RUN3 + {SPR_DOGS,1,2,A_Chase,S_DOGS_RUN5}, // S_DOGS_RUN4 + {SPR_DOGS,2,2,A_Chase,S_DOGS_RUN6}, // S_DOGS_RUN5 + {SPR_DOGS,2,2,A_Chase,S_DOGS_RUN7}, // S_DOGS_RUN6 + {SPR_DOGS,3,2,A_Chase,S_DOGS_RUN8}, // S_DOGS_RUN7 + {SPR_DOGS,3,2,A_Chase,S_DOGS_RUN1}, // S_DOGS_RUN8 + {SPR_DOGS,4,8,A_FaceTarget,S_DOGS_ATK2}, // S_DOGS_ATK1 + {SPR_DOGS,5,8,A_FaceTarget,S_DOGS_ATK3}, // S_DOGS_ATK2 + {SPR_DOGS,6,8,A_SargAttack,S_DOGS_RUN1}, // S_DOGS_ATK3 + {SPR_DOGS,7,2,NULL,S_DOGS_PAIN2}, // S_DOGS_PAIN + {SPR_DOGS,7,2,A_Pain,S_DOGS_RUN1}, // S_DOGS_PAIN2 + {SPR_DOGS,8,8,NULL,S_DOGS_DIE2}, // S_DOGS_DIE1 + {SPR_DOGS,9,8,A_Scream,S_DOGS_DIE3}, // S_DOGS_DIE2 + {SPR_DOGS,10,4,NULL,S_DOGS_DIE4}, // S_DOGS_DIE3 + {SPR_DOGS,11,4,A_Fall,S_DOGS_DIE5}, // S_DOGS_DIE4 + {SPR_DOGS,12,4,NULL,S_DOGS_DIE6}, // S_DOGS_DIE5 + {SPR_DOGS,13,-1,NULL,S_NULL}, // S_DOGS_DIE6 + {SPR_DOGS,13,5,NULL,S_DOGS_RAISE2}, // S_DOGS_RAISE1 + {SPR_DOGS,12,5,NULL,S_DOGS_RAISE3}, // S_DOGS_RAISE2 + {SPR_DOGS,11,5,NULL,S_DOGS_RAISE4}, // S_DOGS_RAISE3 + {SPR_DOGS,10,5,NULL,S_DOGS_RAISE5}, // S_DOGS_RAISE4 + {SPR_DOGS,9,5,NULL,S_DOGS_RAISE6}, // S_DOGS_RAISE5 + {SPR_DOGS,8,5,NULL,S_DOGS_RUN1}, // S_DOGS_RAISE6 + + // add dummy beta bfg / lost soul frames for dehacked compatibility + // fixes bug #1576151 (part 2) + +#define BFGDELAY 1 +#define OLDBFG_1FRAMES(x) {SPR_BFGG,1,BFGDELAY,A_FireOldBFG,x+S_OLDBFG1+2}, +#define OLDBFG_2FRAMES(x) OLDBFG_1FRAMES(x) OLDBFG_1FRAMES(x+1) +#define OLDBFG_4FRAMES(x) OLDBFG_2FRAMES(x) OLDBFG_2FRAMES(x+2) +#define OLDBFG_8FRAMES(x) OLDBFG_4FRAMES(x) OLDBFG_4FRAMES(x+4) + {SPR_BFGG,0,10,A_BFGsound,S_OLDBFG1+1}, // S_OLDBFG1 + + OLDBFG_8FRAMES(0) + OLDBFG_8FRAMES(8) + OLDBFG_8FRAMES(16) + OLDBFG_8FRAMES(24) + OLDBFG_8FRAMES(32) + + //{SPR_DOGS,7,2,A_Pain,S_DOGS_RUN1}, + {SPR_BFGG,1,0,A_Light0,S_OLDBFG43}, // S_OLDBFG42 + {SPR_BFGG,1,20,A_ReFire,S_BFG}, // S_OLDBFG43 + + // killough 7/11/98: end of beta BFG + + // killough 7/19/98: First plasma fireball in the beta: + {SPR_PLS1,32768,6,NULL,S_PLS1BALL2}, // S_PLS1BALL + {SPR_PLS1,32769,6,NULL,S_PLS1BALL}, // S_PLS1BALL2 + {SPR_PLS1,32770,4,NULL,S_PLS1EXP2}, // S_PLS1EXP + {SPR_PLS1,32771,4,NULL,S_PLS1EXP3}, // S_PLS1EXP2 + {SPR_PLS1,32772,4,NULL,S_PLS1EXP4}, // S_PLS1EXP3 + {SPR_PLS1,32773,4,NULL,S_PLS1EXP5}, // S_PLS1EXP4 + {SPR_PLS1,32774,4,NULL,S_NULL}, // S_PLS1EXP5 + + // killough 7/19/98: Second plasma fireball in the beta: + {SPR_PLS2,32768,4,NULL,S_PLS2BALL2}, // S_PLS2BALL + {SPR_PLS2,32769,4,NULL,S_PLS2BALL}, // S_PLS2BALL2 + {SPR_PLS2,32770,6,NULL,S_PLS2BALLX2}, // S_PLS2BALLX1 + {SPR_PLS2,32771,6,NULL,S_PLS2BALLX3}, // S_PLS2BALLX2 + {SPR_PLS2,32772,6,NULL,S_NULL}, // S_PLS2BALLX3 + + {SPR_BON3,0,6,NULL,S_BON3}, // S_BON3 // killough 7/11/98: + {SPR_BON4,0,6,NULL,S_BON4}, // S_BON4 // beta bonus items + + // killough 10/98: beta lost souls attacked from a distance, + // animated with colors, and stayed in the air when killed. + // This is an approximation, but I'm sure it can be improved. + + // spawnstate + {SPR_SKUL,0,10,A_Look,S_BSKUL_STND}, // S_BSKUL_STND + + // chasestate + {SPR_SKUL,1,5,A_Chase,S_BSKUL_RUN2}, // S_BSKUL_RUN1 + {SPR_SKUL,2,5,A_Chase,S_BSKUL_RUN3}, // S_BSKUL_RUN2 + {SPR_SKUL,3,5,A_Chase,S_BSKUL_RUN4}, // S_BSKUL_RUN3 + {SPR_SKUL,0,5,A_Chase,S_BSKUL_RUN1}, // S_BSKUL_RUN4 + + // missilestate + {SPR_SKUL,4,4,A_FaceTarget,S_BSKUL_ATK2}, // S_BSKUL_ATK1 + {SPR_SKUL,5,5,A_BetaSkullAttack,S_BSKUL_ATK3}, // S_BSKUL_ATK2 + {SPR_SKUL,5,4,NULL,S_BSKUL_RUN1}, // S_BSKUL_ATK3 + + // painstate + {SPR_SKUL,6,4,NULL,S_BSKUL_PAIN2}, // S_BSKUL_PAIN1 + {SPR_SKUL,7,2,A_Pain,S_BSKUL_RUN1}, // S_BSKUL_PAIN2 + {SPR_SKUL,8,4,NULL,S_BSKUL_RUN1}, // S_BSKUL_PAIN3 + + // deathstate + {SPR_SKUL, 9,5,NULL,S_BSKUL_DIE2}, // S_BSKUL_DIE1 + {SPR_SKUL,10,5,NULL,S_BSKUL_DIE3}, // S_BSKUL_DIE2 + {SPR_SKUL,11,5,NULL,S_BSKUL_DIE4}, // S_BSKUL_DIE3 + {SPR_SKUL,12,5,NULL,S_BSKUL_DIE5}, // S_BSKUL_DIE4 + {SPR_SKUL,13,5,A_Scream,S_BSKUL_DIE6}, // S_BSKUL_DIE5 + {SPR_SKUL,14,5,NULL,S_BSKUL_DIE7}, // S_BSKUL_DIE6 + {SPR_SKUL,15,5,A_Fall,S_BSKUL_DIE8}, // S_BSKUL_DIE7 + {SPR_SKUL,16,5,A_Stop,S_BSKUL_DIE8}, // S_BSKUL_DIE8 + + // killough 10/98: mushroom effect + {SPR_MISL,32769,8,A_Mushroom,S_EXPLODE2}, // S_MUSHROOM + + {SPR_PLAY,14,5,NULL,S_PLAY_GDIE2,0,0}, // S_PLAY_GDIE1 + {SPR_PLAY,15,5,A_SkullPop,S_PLAY_GDIE3,0,0}, // S_PLAY_GDIE2 + {SPR_PLAY,16,5,A_Fall,S_PLAY_GDIE4,0,0}, // S_PLAY_GDIE3 + {SPR_PLAY,17,5,NULL,S_PLAY_GDIE5,0,0}, // S_PLAY_GDIE4 + {SPR_PLAY,18,5,NULL,S_PLAY_GDIE6,0,0}, // S_PLAY_GDIE5 + {SPR_PLAY,19,5,NULL,S_PLAY_GDIE7,0,0}, // S_PLAY_GDIE6 + {SPR_PLAY,20,5,NULL,S_PLAY_GDIE8,0,0}, // S_PLAY_GDIE7 + {SPR_PLAY,21,5,NULL,S_PLAY_GDIE9,0,0}, // S_PLAY_GDIE8 + {SPR_PLAY,22,-1,NULL,S_NULL,0,0}, // S_PLAY_GDIE9 + +}; + +// ******************************************************************** +// Object "Thing" definitions +// ******************************************************************** +// Now we get to the actual objects and their characteristics. If +// you've seen Dehacked, much of this is where the Bits are set, +// commented below as "flags", as well as where you wire in which +// frames are the beginning frames for near and far attack, death, +// and such. Sounds are hooked in here too, as well as how much +// mass, speed and so forth a Thing has. Everything you ever wanted +// to know... +// +// Like all this other stuff, the MT_* entries are enumerated in info.h +// +// Note that these are all just indices of the elements involved, and +// not real pointers to them. For example, the player's death sequence +// is S_PLAY_DIE1, which just evaluates to the index in the states[] +// array above, which actually knows what happens then and what the +// sprite looks like, if it makes noise or not, etc. +// +// Additional comments about each of the entries are located in info.h +// next to the mobjinfo_t structure definition. +// +// This goes on for the next 3000+ lines... + +mobjinfo_t mobjinfo[NUMMOBJTYPES] = { + { // MT_PLAYER + -1, // doomednum + S_PLAY, // spawnstate + 100, // spawnhealth + S_PLAY_RUN1, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_PLAY_PAIN, // painstate + 255, // painchance + sfx_plpain, // painsound + S_NULL, // meleestate + S_PLAY_ATK1, // missilestate + S_PLAY_DIE1, // deathstate + S_PLAY_XDIE1, // xdeathstate + sfx_pldeth, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_POSSESSED + 3004, // doomednum + S_POSS_STND, // spawnstate + 20, // spawnhealth + S_POSS_RUN1, // seestate + sfx_posit1, // seesound + 8, // reactiontime + sfx_pistol, // attacksound + S_POSS_PAIN, // painstate + 200, // painchance + sfx_popain, // painsound + 0, // meleestate + S_POSS_ATK1, // missilestate + S_POSS_DIE1, // deathstate + S_POSS_XDIE1, // xdeathstate + sfx_podth1, // deathsound + 8, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_POSS_RAISE1 // raisestate + }, + + { // MT_SHOTGUY + 9, // doomednum + S_SPOS_STND, // spawnstate + 30, // spawnhealth + S_SPOS_RUN1, // seestate + sfx_posit2, // seesound + 8, // reactiontime + 0, // attacksound + S_SPOS_PAIN, // painstate + 170, // painchance + sfx_popain, // painsound + 0, // meleestate + S_SPOS_ATK1, // missilestate + S_SPOS_DIE1, // deathstate + S_SPOS_XDIE1, // xdeathstate + sfx_podth2, // deathsound + 8, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_SPOS_RAISE1 // raisestate + }, + + { // MT_VILE + 64, // doomednum + S_VILE_STND, // spawnstate + 700, // spawnhealth + S_VILE_RUN1, // seestate + sfx_vilsit, // seesound + 8, // reactiontime + 0, // attacksound + S_VILE_PAIN, // painstate + 10, // painchance + sfx_vipain, // painsound + 0, // meleestate + S_VILE_ATK1, // missilestate + S_VILE_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_vildth, // deathsound + 15, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 500, // mass + 0, // damage + sfx_vilact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_FIRE + -1, // doomednum + S_FIRE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_UNDEAD + 66, // doomednum + S_SKEL_STND, // spawnstate + 300, // spawnhealth + S_SKEL_RUN1, // seestate + sfx_skesit, // seesound + 8, // reactiontime + 0, // attacksound + S_SKEL_PAIN, // painstate + 100, // painchance + sfx_popain, // painsound + S_SKEL_FIST1, // meleestate + S_SKEL_MISS1, // missilestate + S_SKEL_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_skedth, // deathsound + 10, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 500, // mass + 0, // damage + sfx_skeact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_SKEL_RAISE1 // raisestate + }, + + { // MT_TRACER + -1, // doomednum + S_TRACER, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_skeatk, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_TRACEEXP1, // deathstate + S_NULL, // xdeathstate + sfx_barexp, // deathsound + 10*FRACUNIT, // speed + 11*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 10, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_SMOKE + -1, // doomednum + S_SMOKE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_FATSO + 67, // doomednum + S_FATT_STND, // spawnstate + 600, // spawnhealth + S_FATT_RUN1, // seestate + sfx_mansit, // seesound + 8, // reactiontime + 0, // attacksound + S_FATT_PAIN, // painstate + 80, // painchance + sfx_mnpain, // painsound + 0, // meleestate + S_FATT_ATK1, // missilestate + S_FATT_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_mandth, // deathsound + 8, // speed + 48*FRACUNIT, // radius + 64*FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_FATT_RAISE1 // raisestate + }, + + { // MT_FATSHOT + -1, // doomednum + S_FATSHOT1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_FATSHOTX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 20*FRACUNIT, // speed + 6*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 8, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags \\ killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_CHAINGUY + 65, // doomednum + S_CPOS_STND, // spawnstate + 70, // spawnhealth + S_CPOS_RUN1, // seestate + sfx_posit2, // seesound + 8, // reactiontime + 0, // attacksound + S_CPOS_PAIN, // painstate + 170, // painchance + sfx_popain, // painsound + 0, // meleestate + S_CPOS_ATK1, // missilestate + S_CPOS_DIE1, // deathstate + S_CPOS_XDIE1, // xdeathstate + sfx_podth2, // deathsound + 8, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_CPOS_RAISE1 // raisestate + }, + + { // MT_TROOP + 3001, // doomednum + S_TROO_STND, // spawnstate + 60, // spawnhealth + S_TROO_RUN1, // seestate + sfx_bgsit1, // seesound + 8, // reactiontime + 0, // attacksound + S_TROO_PAIN, // painstate + 200, // painchance + sfx_popain, // painsound + S_TROO_ATK1, // meleestate + S_TROO_ATK1, // missilestate + S_TROO_DIE1, // deathstate + S_TROO_XDIE1, // xdeathstate + sfx_bgdth1, // deathsound + 8, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_bgact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // killough |MF_TRANSLUCENT, // flags // phares + S_TROO_RAISE1 // raisestate + }, + + { // MT_SERGEANT + 3002, // doomednum + S_SARG_STND, // spawnstate + 150, // spawnhealth + S_SARG_RUN1, // seestate + sfx_sgtsit, // seesound + 8, // reactiontime + sfx_sgtatk, // attacksound + S_SARG_PAIN, // painstate + 180, // painchance + sfx_dmpain, // painsound + S_SARG_ATK1, // meleestate + 0, // missilestate + S_SARG_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_sgtdth, // deathsound + 10, // speed + 30*FRACUNIT, // radius + 56*FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_SARG_RAISE1 // raisestate + }, + + { // MT_SHADOWS + 58, // doomednum + S_SARG_STND, // spawnstate + 150, // spawnhealth + S_SARG_RUN1, // seestate + sfx_sgtsit, // seesound + 8, // reactiontime + sfx_sgtatk, // attacksound + S_SARG_PAIN, // painstate + 180, // painchance + sfx_dmpain, // painsound + S_SARG_ATK1, // meleestate + 0, // missilestate + S_SARG_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_sgtdth, // deathsound + 10, // speed + 30*FRACUNIT, // radius + 56*FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_SHADOW|MF_COUNTKILL, // flags + S_SARG_RAISE1 // raisestate + }, + + { // MT_HEAD + 3005, // doomednum + S_HEAD_STND, // spawnstate + 400, // spawnhealth + S_HEAD_RUN1, // seestate + sfx_cacsit, // seesound + 8, // reactiontime + 0, // attacksound + S_HEAD_PAIN, // painstate + 128, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_HEAD_ATK1, // missilestate + S_HEAD_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_cacdth, // deathsound + 8, // speed + 31*FRACUNIT, // radius + 56*FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY|MF_COUNTKILL, // flags + S_HEAD_RAISE1 // raisestate + }, + + { // MT_BRUISER + 3003, // doomednum + S_BOSS_STND, // spawnstate + 1000, // spawnhealth + S_BOSS_RUN1, // seestate + sfx_brssit, // seesound + 8, // reactiontime + 0, // attacksound + S_BOSS_PAIN, // painstate + 50, // painchance + sfx_dmpain, // painsound + S_BOSS_ATK1, // meleestate + S_BOSS_ATK1, // missilestate + S_BOSS_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_brsdth, // deathsound + 8, // speed + 24*FRACUNIT, // radius + 64*FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_BOSS_RAISE1 // raisestate + }, + + { // MT_BRUISERSHOT + -1, // doomednum + S_BRBALL1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BRBALLX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 15*FRACUNIT, // speed + 6*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 8, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_KNIGHT + 69, // doomednum + S_BOS2_STND, // spawnstate + 500, // spawnhealth + S_BOS2_RUN1, // seestate + sfx_kntsit, // seesound + 8, // reactiontime + 0, // attacksound + S_BOS2_PAIN, // painstate + 50, // painchance + sfx_dmpain, // painsound + S_BOS2_ATK1, // meleestate + S_BOS2_ATK1, // missilestate + S_BOS2_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_kntdth, // deathsound + 8, // speed + 24*FRACUNIT, // radius + 64*FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_BOS2_RAISE1 // raisestate + }, + + { // MT_SKULL + 3006, // doomednum + S_SKULL_STND, // spawnstate + 100, // spawnhealth + S_SKULL_RUN1, // seestate + 0, // seesound + 8, // reactiontime + sfx_sklatk, // attacksound + S_SKULL_PAIN, // painstate + 256, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_SKULL_ATK1, // missilestate + S_SKULL_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 8, // speed + 16*FRACUNIT, // radius + 56*FRACUNIT, // height + 50, // mass + 3, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_SPIDER + 7, // doomednum + S_SPID_STND, // spawnstate + 3000, // spawnhealth + S_SPID_RUN1, // seestate + sfx_spisit, // seesound + 8, // reactiontime + sfx_shotgn, // attacksound + S_SPID_PAIN, // painstate + 40, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_SPID_ATK1, // missilestate + S_SPID_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_spidth, // deathsound + 12, // speed + 128*FRACUNIT, // radius + 100*FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_BABY + 68, // doomednum + S_BSPI_STND, // spawnstate + 500, // spawnhealth + S_BSPI_SIGHT, // seestate + sfx_bspsit, // seesound + 8, // reactiontime + 0, // attacksound + S_BSPI_PAIN, // painstate + 128, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_BSPI_ATK1, // missilestate + S_BSPI_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_bspdth, // deathsound + 12, // speed + 64*FRACUNIT, // radius + 64*FRACUNIT, // height + 600, // mass + 0, // damage + sfx_bspact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_BSPI_RAISE1 // raisestate + }, + + { // MT_CYBORG + 16, // doomednum + S_CYBER_STND, // spawnstate + 4000, // spawnhealth + S_CYBER_RUN1, // seestate + sfx_cybsit, // seesound + 8, // reactiontime + 0, // attacksound + S_CYBER_PAIN, // painstate + 20, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_CYBER_ATK1, // missilestate + S_CYBER_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_cybdth, // deathsound + 16, // speed + 40*FRACUNIT, // radius + 110*FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_PAIN + 71, // doomednum + S_PAIN_STND, // spawnstate + 400, // spawnhealth + S_PAIN_RUN1, // seestate + sfx_pesit, // seesound + 8, // reactiontime + 0, // attacksound + S_PAIN_PAIN, // painstate + 128, // painchance + sfx_pepain, // painsound + 0, // meleestate + S_PAIN_ATK1, // missilestate + S_PAIN_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_pedth, // deathsound + 8, // speed + 31*FRACUNIT, // radius + 56*FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY|MF_COUNTKILL, // flags + S_PAIN_RAISE1 // raisestate + }, + + { // MT_WOLFSS + 84, // doomednum + S_SSWV_STND, // spawnstate + 50, // spawnhealth + S_SSWV_RUN1, // seestate + sfx_sssit, // seesound + 8, // reactiontime + 0, // attacksound + S_SSWV_PAIN, // painstate + 170, // painchance + sfx_popain, // painsound + 0, // meleestate + S_SSWV_ATK1, // missilestate + S_SSWV_DIE1, // deathstate + S_SSWV_XDIE1, // xdeathstate + sfx_ssdth, // deathsound + 8, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_SSWV_RAISE1 // raisestate + }, + + { // MT_KEEN + 72, // doomednum + S_KEENSTND, // spawnstate + 100, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_KEENPAIN, // painstate + 256, // painchance + sfx_keenpn, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_COMMKEEN, // deathstate + S_NULL, // xdeathstate + sfx_keendt, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 72*FRACUNIT, // height + 10000000, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_BOSSBRAIN + 88, // doomednum + S_BRAIN, // spawnstate + 250, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_BRAIN_PAIN, // painstate + 255, // painchance + sfx_bospn, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BRAIN_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_bosdth, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 10000000, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SHOOTABLE, // flags + S_NULL // raisestate + }, + + { // MT_BOSSSPIT + 89, // doomednum + S_BRAINEYE, // spawnstate + 1000, // spawnhealth + S_BRAINEYESEE, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 32*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOSECTOR, // flags + S_NULL // raisestate + }, + + { // MT_BOSSTARGET + 87, // doomednum + S_NULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 32*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOSECTOR, // flags + S_NULL // raisestate + }, + + { // MT_SPAWNSHOT + -1, // doomednum + S_SPAWN1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_bospit, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 10*FRACUNIT, // speed + 6*FRACUNIT, // radius + 32*FRACUNIT, // height + 100, // mass + 3, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_NOCLIP, // flags + S_NULL // raisestate + }, + + { // MT_SPAWNFIRE + -1, // doomednum + S_SPAWNFIRE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_BARREL + 2035, // doomednum + S_BAR1, // spawnstate + 20, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BEXP, // deathstate + S_NULL, // xdeathstate + sfx_barexp, // deathsound + 0, // speed + 10*FRACUNIT, // radius + 42*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD, // flags + S_NULL // raisestate + }, + + { // MT_TROOPSHOT + -1, // doomednum + S_TBALL1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_TBALLX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 10*FRACUNIT, // speed + 6*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 3, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_HEADSHOT + -1, // doomednum + S_RBALL1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_RBALLX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 10*FRACUNIT, // speed + 6*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 5, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares, // flags + S_NULL // raisestate + }, + + { // MT_ROCKET + -1, // doomednum + S_ROCKET, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_rlaunc, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_EXPLODE1, // deathstate + S_NULL, // xdeathstate + sfx_barexp, // deathsound + 20*FRACUNIT, // speed + 11*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 20, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_PLASMA + -1, // doomednum + S_PLASBALL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_plasma, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_PLASEXP, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 25*FRACUNIT, // speed + 13*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 5, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_BFG + -1, // doomednum + S_BFGSHOT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + 0, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BFGLAND, // deathstate + S_NULL, // xdeathstate + sfx_rxplod, // deathsound + 25*FRACUNIT, // speed + 13*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 100, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_ARACHPLAZ + -1, // doomednum + S_ARACH_PLAZ, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_plasma, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_ARACH_PLEX, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 25*FRACUNIT, // speed + 13*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 5, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_PUFF + -1, // doomednum + S_PUFF1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_BLOOD + -1, // doomednum + S_BLOOD1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_TFOG + -1, // doomednum + S_TFOG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_IFOG + -1, // doomednum + S_IFOG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_TELEPORTMAN + 14, // doomednum + S_NULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOSECTOR, // flags + S_NULL // raisestate + }, + + { // MT_EXTRABFG + -1, // doomednum + S_BFGEXP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC0 + 2018, // doomednum + S_ARM1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC1 + 2019, // doomednum + S_ARM2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC2 + 2014, // doomednum + S_BON1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC3 + 2015, // doomednum + S_BON2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC4 + 5, // doomednum + S_BKEY, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC5 + 13, // doomednum + S_RKEY, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC6 + 6, // doomednum + S_YKEY, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC7 + 39, // doomednum + S_YSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC8 + 38, // doomednum + S_RSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC9 + 40, // doomednum + S_BSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC10 + 2011, // doomednum + S_STIM, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC11 + 2012, // doomednum + S_MEDI, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC12 + 2013, // doomednum + S_SOUL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM|MF_TRANSLUCENT, // flags // killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_INV + 2022, // doomednum + S_PINV, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM|MF_TRANSLUCENT, // flags // killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_MISC13 + 2023, // doomednum + S_PSTR, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_INS + 2024, // doomednum + S_PINS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM|MF_TRANSLUCENT, // flags // killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_MISC14 + 2025, // doomednum + S_SUIT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC15 + 2026, // doomednum + S_PMAP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC16 + 2045, // doomednum + S_PVIS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MEGA + 83, // doomednum + S_MEGA, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM|MF_TRANSLUCENT, // flags // killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_CLIP + 2007, // doomednum + S_CLIP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC17 + 2048, // doomednum + S_AMMO, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC18 + 2010, // doomednum + S_ROCK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC19 + 2046, // doomednum + S_BROK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC20 + 2047, // doomednum + S_CELL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC21 + 17, // doomednum + S_CELP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC22 + 2008, // doomednum + S_SHEL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC23 + 2049, // doomednum + S_SBOX, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC24 + 8, // doomednum + S_BPAK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC25 + 2006, // doomednum + S_BFUG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_CHAINGUN + 2002, // doomednum + S_MGUN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC26 + 2005, // doomednum + S_CSAW, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC27 + 2003, // doomednum + S_LAUN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC28 + 2004, // doomednum + S_PLAS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_SHOTGUN + 2001, // doomednum + S_SHOT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_SUPERSHOTGUN + 82, // doomednum + S_SHOT2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC29 + 85, // doomednum + S_TECHLAMP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC30 + 86, // doomednum + S_TECH2LAMP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC31 + 2028, // doomednum + S_COLU, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC32 + 30, // doomednum + S_TALLGRNCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC33 + 31, // doomednum + S_SHRTGRNCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC34 + 32, // doomednum + S_TALLREDCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC35 + 33, // doomednum + S_SHRTREDCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC36 + 37, // doomednum + S_SKULLCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC37 + 36, // doomednum + S_HEARTCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC38 + 41, // doomednum + S_EVILEYE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC39 + 42, // doomednum + S_FLOATSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC40 + 43, // doomednum + S_TORCHTREE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC41 + 44, // doomednum + S_BLUETORCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC42 + 45, // doomednum + S_GREENTORCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC43 + 46, // doomednum + S_REDTORCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC44 + 55, // doomednum + S_BTORCHSHRT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC45 + 56, // doomednum + S_GTORCHSHRT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC46 + 57, // doomednum + S_RTORCHSHRT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC47 + 47, // doomednum + S_STALAGTITE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC48 + 48, // doomednum + S_TECHPILLAR, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC49 + 34, // doomednum + S_CANDLESTIK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC50 + 35, // doomednum + S_CANDELABRA, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC51 + 49, // doomednum + S_BLOODYTWITCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 68*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC52 + 50, // doomednum + S_MEAT2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 84*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC53 + 51, // doomednum + S_MEAT3, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 84*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC54 + 52, // doomednum + S_MEAT4, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 68*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC55 + 53, // doomednum + S_MEAT5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 52*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC56 + 59, // doomednum + S_MEAT2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 84*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC57 + 60, // doomednum + S_MEAT4, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 68*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC58 + 61, // doomednum + S_MEAT3, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 52*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC59 + 62, // doomednum + S_MEAT5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 52*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC60 + 63, // doomednum + S_BLOODYTWITCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 68*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC61 + 22, // doomednum + S_HEAD_DIE6, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC62 + 15, // doomednum + S_PLAY_DIE7, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC63 + 18, // doomednum + S_POSS_DIE5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC64 + 21, // doomednum + S_SARG_DIE6, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC65 + 23, // doomednum + S_SKULL_DIE6, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC66 + 20, // doomednum + S_TROO_DIE5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC67 + 19, // doomednum + S_SPOS_DIE5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC68 + 10, // doomednum + S_PLAY_XDIE9, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC69 + 12, // doomednum + S_PLAY_XDIE9, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC70 + 28, // doomednum + S_HEADSONSTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC71 + 24, // doomednum + S_GIBS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC72 + 27, // doomednum + S_HEADONASTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC73 + 29, // doomednum + S_HEADCANDLES, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC74 + 25, // doomednum + S_DEADSTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC75 + 26, // doomednum + S_LIVESTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC76 + 54, // doomednum + S_BIGTREE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 32*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC77 + 70, // doomednum + S_BBAR1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC78 + 73, // doomednum + S_HANGNOGUTS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 88*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC79 + 74, // doomednum + S_HANGBNOBRAIN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 88*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC80 + 75, // doomednum + S_HANGTLOOKDN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 64*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC81 + 76, // doomednum + S_HANGTSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 64*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC82 + 77, // doomednum + S_HANGTLOOKUP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 64*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC83 + 78, // doomednum + S_HANGTNOBRAIN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 64*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC84 + 79, // doomednum + S_COLONGIBS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_MISC85 + 80, // doomednum + S_SMALLPOOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_MISC86 + 81, // doomednum + S_BRAINSTEM, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + // For use with wind and current effects + { // MT_PUSH // phares + 5001, // doomednum // | //jff 5/11/98 deconflict + S_TNT1, // spawnstate // V // with DOSDoom + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8, // radius + 8, // height + 10, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + // For use with wind and current effects + { // MT_PULL + 5002, // doomednum //jff 5/11/98 deconflict + S_TNT1, // spawnstate // with DOSDoom + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8, // radius + 8, // height + 10, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + // Marine's best friend :) // killough 7/19/98 + { // MT_DOGS + 888, // doomednum + S_DOGS_STND, // spawnstate + 500, // spawnhealth + S_DOGS_RUN1, // seestate + sfx_dgsit, // seesound + 8, // reactiontime + sfx_dgatk, // attacksound + S_DOGS_PAIN, // painstate + 180, // painchance + sfx_dgpain, // painsound + S_DOGS_ATK1, // meleestate + 0, // missilestate + S_DOGS_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_dgdth, // deathsound + 10, // speed + 12*FRACUNIT, // radius + 28*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_dgact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_DOGS_RAISE1 // raisestate + }, + + // killough 7/11/98: this is the first of two plasma fireballs in the beta + { // MT_PLASMA1 + -1, // doomednum + S_PLS1BALL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_plasma, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_PLS1EXP, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 25*FRACUNIT, // speed + 13*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 4, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_BOUNCES, // flags + S_NULL // raisestate + }, + + // killough 7/11/98: this is the second of two plasma fireballs in the beta + { // MT_PLASMA2 + -1, // doomednum + S_PLS2BALL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_plasma, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_PLS2BALLX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 25*FRACUNIT, // speed + 6*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 4, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_BOUNCES, // flags + S_NULL // raisestate + }, + + // killough 7/11/98: this is the evil sceptre in the beta version + { // MT_SCEPTRE + // COMPAT: MBF sets doomednum to -1 if not in "beta emulation" mode, + // causing MT_SCEPTRE map things to not getting spawned at all + 2016, // doomednum + S_BON3, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 10*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + // killough 7/11/98: this is the unholy bible in the beta version + { // MT_BIBLE + // COMPAT: MBF sets doomednum to -1 if not in "beta emulation" mode, + // causing MT_BIBLE map things to not getting spawned at all + 2017, // doomednum + S_BON4, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 10*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + + // MUSINFO lump + { // MT_MUSICSOURCE + 14164, // doomednum + S_TNT1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16, // radius + 16, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_GIBDTH + -1, // doomednum + S_TNT1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 4*FRACUNIT, // radius + 4*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_DROPOFF, // flags + S_NULL // raisestate + }, + +}; diff --git a/src/info.h b/src/info.h new file mode 100644 index 0000000..9bf26ed --- /dev/null +++ b/src/info.h @@ -0,0 +1,1552 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Thing frame/state LUT, + * generated by multigen utilitiy. + * This one is the original DOOM version, preserved. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __INFO__ +#define __INFO__ + +/* Needed for action function pointer handling. */ +#include "d_think.h" + +/******************************************************************** + * Sprite name enumeration - must match info.c * + ********************************************************************/ +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, + SPR_TNT1, /* add invisible sprite phares 3/8/98 */ + SPR_DOGS, /* killough 7/19/98: Marine's best friend :) */ + SPR_PLS1, // killough 7/19/98: first of two plasma fireballs in the beta + SPR_PLS2, // killough 7/19/98: second of two plasma fireballs in the beta + SPR_BON3, // killough 7/11/98: evil sceptre in beta version + SPR_BON4, // killough 7/11/98: unholy bible in beta version + SPR_BLD2, // blood splats from Doom Retro, unsused in PrBoom+ + + // 100 extra sprite names to use in dehacked patches + SPR_SP00, SPR_SP01, SPR_SP02, SPR_SP03, SPR_SP04, SPR_SP05, SPR_SP06, SPR_SP07, SPR_SP08, SPR_SP09, + SPR_SP10, SPR_SP11, SPR_SP12, SPR_SP13, SPR_SP14, SPR_SP15, SPR_SP16, SPR_SP17, SPR_SP18, SPR_SP19, + SPR_SP20, SPR_SP21, SPR_SP22, SPR_SP23, SPR_SP24, SPR_SP25, SPR_SP26, SPR_SP27, SPR_SP28, SPR_SP29, + SPR_SP30, SPR_SP31, SPR_SP32, SPR_SP33, SPR_SP34, SPR_SP35, SPR_SP36, SPR_SP37, SPR_SP38, SPR_SP39, + SPR_SP40, SPR_SP41, SPR_SP42, SPR_SP43, SPR_SP44, SPR_SP45, SPR_SP46, SPR_SP47, SPR_SP48, SPR_SP49, + SPR_SP50, SPR_SP51, SPR_SP52, SPR_SP53, SPR_SP54, SPR_SP55, SPR_SP56, SPR_SP57, SPR_SP58, SPR_SP59, + SPR_SP60, SPR_SP61, SPR_SP62, SPR_SP63, SPR_SP64, SPR_SP65, SPR_SP66, SPR_SP67, SPR_SP68, SPR_SP69, + SPR_SP70, SPR_SP71, SPR_SP72, SPR_SP73, SPR_SP74, SPR_SP75, SPR_SP76, SPR_SP77, SPR_SP78, SPR_SP79, + SPR_SP80, SPR_SP81, SPR_SP82, SPR_SP83, SPR_SP84, SPR_SP85, SPR_SP86, SPR_SP87, SPR_SP88, SPR_SP89, + SPR_SP90, SPR_SP91, SPR_SP92, SPR_SP93, SPR_SP94, SPR_SP95, SPR_SP96, SPR_SP97, SPR_SP98, SPR_SP99, + + NUMSPRITES /* counter of how many there are */ + +} spritenum_t; + +/******************************************************************** + * States (frames) enumeration -- must match info.c * + ********************************************************************/ + +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, + S_TNT1, /* add state for invisible sprite phares 3/8/98 */ + + S_GRENADE, /* killough 8/9/98: grenade launcher */ + S_DETONATE, /* killough 8/9/98: detonation of objects */ + S_DETONATE2, + S_DETONATE3, + + // always count dog states, even if dogs are disabled + S_DOGS_STND, /* killough 7/19/98: Marine's best friend :) */ + S_DOGS_STND2, + S_DOGS_RUN1, + S_DOGS_RUN2, + S_DOGS_RUN3, + S_DOGS_RUN4, + S_DOGS_RUN5, + S_DOGS_RUN6, + S_DOGS_RUN7, + S_DOGS_RUN8, + S_DOGS_ATK1, + S_DOGS_ATK2, + S_DOGS_ATK3, + S_DOGS_PAIN, + S_DOGS_PAIN2, + S_DOGS_DIE1, + S_DOGS_DIE2, + S_DOGS_DIE3, + S_DOGS_DIE4, + S_DOGS_DIE5, + S_DOGS_DIE6, + S_DOGS_RAISE1, + S_DOGS_RAISE2, + S_DOGS_RAISE3, + S_DOGS_RAISE4, + S_DOGS_RAISE5, + S_DOGS_RAISE6, + + // add dummy beta bfg / lost soul frames for dehacked compatibility + // fixes bug #1576151 (part 2) + S_OLDBFG1, // killough 7/11/98: the old BFG's 43 firing frames + S_OLDBFG42 = S_OLDBFG1+41, + S_OLDBFG43, + + S_PLS1BALL, // killough 7/19/98: first plasma fireball in the beta + S_PLS1BALL2, + S_PLS1EXP, + S_PLS1EXP2, + S_PLS1EXP3, + S_PLS1EXP4, + S_PLS1EXP5, + + S_PLS2BALL, // killough 7/19/98: second plasma fireball in the beta + S_PLS2BALL2, + S_PLS2BALLX1, + S_PLS2BALLX2, + S_PLS2BALLX3, + S_BON3, // killough 7/11/98: evil sceptre in beta version + S_BON4, // killough 7/11/98: unholy bible in beta version + + // killough 10/98: beta lost souls were different from their modern cousins + S_BSKUL_STND, + S_BSKUL_RUN1, + S_BSKUL_RUN2, + S_BSKUL_RUN3, + S_BSKUL_RUN4, + S_BSKUL_ATK1, + S_BSKUL_ATK2, + S_BSKUL_ATK3, + S_BSKUL_PAIN1, + S_BSKUL_PAIN2, + S_BSKUL_PAIN3, + S_BSKUL_DIE1, + S_BSKUL_DIE2, + S_BSKUL_DIE3, + S_BSKUL_DIE4, + S_BSKUL_DIE5, + S_BSKUL_DIE6, + S_BSKUL_DIE7, + S_BSKUL_DIE8, + + S_MUSHROOM, /* killough 10/98: mushroom explosion effect */ + + S_PLAY_GDIE1, + S_PLAY_GDIE2, + S_PLAY_GDIE3, + S_PLAY_GDIE4, + S_PLAY_GDIE5, + S_PLAY_GDIE6, + S_PLAY_GDIE7, + S_PLAY_GDIE8, + S_PLAY_GDIE9, + + EXTRASTATES = 1089, // extra dehacked states + NUMSTATES = 4000 /* Counter of how many there are */ + +} statenum_t; + +/******************************************************************** + * Definition of the state (frames) structure * + ********************************************************************/ + +typedef struct +{ + spritenum_t sprite; /* sprite number to show */ + long frame; /* which frame/subframe of the sprite is shown */ + long tics; /* number of gametics this frame should last */ + actionf_t action; /* code pointer to function for action if any */ + statenum_t nextstate; /* linked list pointer to next state or zero */ + long misc1, misc2; /* apparently never used in DOOM */ +} state_t; + +/* these are in info.c */ +extern state_t states[NUMSTATES]; +extern const char *sprnames[]; /* 1/17/98 killough - CPhipps - const */ + +/******************************************************************** + * Thing enumeration -- must match info.c * + ******************************************************************** + * Note that many of these are generically named for the ornamentals + */ + +typedef enum { + MT_NULL = -1, // ferk: null/invalid mobj (zero is reserved for MT_PLAYER) + 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, + MT_PUSH, /* controls push source - phares */ + MT_PULL, /* controls pull source - phares 3/20/98 */ + MT_DOGS, /* killough 7/19/98: Marine's best friend */ + MT_PLASMA1, // killough 7/11/98: first of alternating beta plasma fireballs + MT_PLASMA2, // killough 7/11/98: second of alternating beta plasma fireballs + MT_SCEPTRE, // killough 7/11/98: evil sceptre in beta version + MT_BIBLE, // killough 7/11/98: unholy bible in beta version + + MT_MUSICSOURCE, /* MUSINFO lump */ + MT_GIBDTH, + + /* proff 11/22/98: Andy Baker's stealth monsters (next 12) + * cph - moved below the MBF stuff, no need to displace them */ + MT_STEALTHBABY, + MT_STEALTHVILE, + MT_STEALTHBRUISER, + MT_STEALTHHEAD, + MT_STEALTHCHAINGUY, + MT_STEALTHSERGEANT, + MT_STEALTHKNIGHT, + MT_STEALTHIMP, + MT_STEALTHFATSO, + MT_STEALTHUNDEAD, + MT_STEALTHSHOTGUY, + MT_STEALTHZOMBIE, + + // 100 extra mobjs to use in dehacked patches + MT_EXTRA00 = 150, MT_EXTRA01, MT_EXTRA02, MT_EXTRA03, MT_EXTRA04, + MT_EXTRA05, MT_EXTRA06, MT_EXTRA07, MT_EXTRA08, MT_EXTRA09, + MT_EXTRA10, MT_EXTRA11, MT_EXTRA12, MT_EXTRA13, MT_EXTRA14, + MT_EXTRA15, MT_EXTRA16, MT_EXTRA17, MT_EXTRA18, MT_EXTRA19, + MT_EXTRA20, MT_EXTRA21, MT_EXTRA22, MT_EXTRA23, MT_EXTRA24, + MT_EXTRA25, MT_EXTRA26, MT_EXTRA27, MT_EXTRA28, MT_EXTRA29, + MT_EXTRA30, MT_EXTRA31, MT_EXTRA32, MT_EXTRA33, MT_EXTRA34, + MT_EXTRA35, MT_EXTRA36, MT_EXTRA37, MT_EXTRA38, MT_EXTRA39, + MT_EXTRA40, MT_EXTRA41, MT_EXTRA42, MT_EXTRA43, MT_EXTRA44, + MT_EXTRA45, MT_EXTRA46, MT_EXTRA47, MT_EXTRA48, MT_EXTRA49, + MT_EXTRA50, MT_EXTRA51, MT_EXTRA52, MT_EXTRA53, MT_EXTRA54, + MT_EXTRA55, MT_EXTRA56, MT_EXTRA57, MT_EXTRA58, MT_EXTRA59, + MT_EXTRA60, MT_EXTRA61, MT_EXTRA62, MT_EXTRA63, MT_EXTRA64, + MT_EXTRA65, MT_EXTRA66, MT_EXTRA67, MT_EXTRA68, MT_EXTRA69, + MT_EXTRA70, MT_EXTRA71, MT_EXTRA72, MT_EXTRA73, MT_EXTRA74, + MT_EXTRA75, MT_EXTRA76, MT_EXTRA77, MT_EXTRA78, MT_EXTRA79, + MT_EXTRA80, MT_EXTRA81, MT_EXTRA82, MT_EXTRA83, MT_EXTRA84, + MT_EXTRA85, MT_EXTRA86, MT_EXTRA87, MT_EXTRA88, MT_EXTRA89, + MT_EXTRA90, MT_EXTRA91, MT_EXTRA92, MT_EXTRA93, MT_EXTRA94, + MT_EXTRA95, MT_EXTRA96, MT_EXTRA97, MT_EXTRA98, MT_EXTRA99, + + NUMMOBJTYPES // Counter of how many there are +} mobjtype_t; + +/******************************************************************** + * Definition of the Thing structure + ********************************************************************/ +/* Note that these are only indices to the state, sound, etc. arrays + * and not actual pointers. Most can be set to zero if the action or + * sound doesn't apply (like lamps generally don't attack or whistle). + */ + +typedef struct +{ + int doomednum; /* Thing number used in id's editor, and now + probably by every other editor too */ + int spawnstate; /* The state (frame) index when this Thing is + first created */ + int spawnhealth; /* The initial hit points for this Thing */ + int seestate; /* The state when it sees you or wakes up */ + int seesound; /* The sound it makes when waking */ + int reactiontime; /* How many tics it waits after it wakes up + before it will start to attack, in normal + skills (halved for nightmare) */ + int attacksound; /* The sound it makes when it attacks */ + int painstate; /* The state to indicate pain */ + int painchance; /* A number that is checked against a random + number 0-255 to see if the Thing is supposed + to go to its painstate or not. Note this + has absolutely nothing to do with the chance + it will get hurt, just the chance of it + reacting visibly. */ + int painsound; /* The sound it emits when it feels pain */ + int meleestate; /* Melee==close attack */ + int missilestate; /* What states to use when it's in the air, if + in fact it is ever used as a missile */ + int deathstate; /* What state begins the death sequence */ + int xdeathstate; /* What state begins the horrible death sequence + like when a rocket takes out a trooper */ + int deathsound; /* The death sound. See also A_Scream() in + p_enemy.c for some tweaking that goes on + for certain monsters */ + int speed; /* How fast it moves. Too fast and it can miss + collision logic. */ + int radius; /* An often incorrect radius */ + int height; /* An often incorrect height, used only to see + if a monster can enter a sector */ + int mass; /* How much an impact will move it. Cacodemons + seem to retreat when shot because they have + very little mass and are moved by impact */ + int damage; /* If this is a missile, how much does it hurt? */ + int activesound; /* What sound it makes wandering around, once + in a while. Chance is 3/256 it will. */ + uint_64_t flags; /* Bit masks for lots of things. See p_mobj.h */ + int raisestate; /* The first state for an Archvile or respawn + resurrection. Zero means it won't come + back to life. */ + mobjtype_t droppeditem; /* ferk: Mobj to drop after death */ + int bloodcolor; /* [FG] colored blood and gibs */ +} mobjinfo_t; + +/* See p_mobj_h for addition more technical info */ +extern mobjinfo_t mobjinfo[NUMMOBJTYPES]; + +#endif diff --git a/src/lprintf.c b/src/lprintf.c new file mode 100644 index 0000000..0f39daf --- /dev/null +++ b/src/lprintf.c @@ -0,0 +1,182 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Provides a logical console output routine that allows what is + * output to console normally and when output is redirected to + * be controlled.. + * + *-----------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef _MSC_VER +#include +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "doomtype.h" +#include "lprintf.h" +#include "i_main.h" +#include "i_system.h" +#include "m_argv.h" +#include "e6y.h"//e6y +#include "i_capture.h" + +int cons_error_mask = -1-LO_INFO; /* all but LO_INFO when redir'd */ +int cons_output_mask = -1; /* all output enabled */ + +/* cphipps - enlarged message buffer and made non-static + * We still have to be careful here, this function can be called after exit + */ +#define MAX_MESSAGE_SIZE 2048 + +int lprintf(OutputLevels pri, const char *s, ...) +{ + int r=0; + char msg[MAX_MESSAGE_SIZE]; + int lvl=pri; + + va_list v; + va_start(v,s); + doom_vsnprintf(msg,sizeof(msg),s,v); /* print message in buffer */ + va_end(v); + + if (lvl&cons_output_mask) /* mask output as specified */ + { +#ifdef _WIN32 + // do not crash with unicode dirs + if (fileno(stdout) != -1) +#endif + r=fprintf(stdout,"%s",msg); + } + if (!isatty(1) && lvl&cons_error_mask) /* if stdout redirected */ + r=fprintf(stderr,"%s",msg); /* select output at console */ + + return r; +} + +/* + * I_Error + * + * cphipps - moved out of i_* headers, to minimise source files that depend on + * the low-level headers. All this does is print the error, then call the + * low-level safe exit function. + * killough 3/20/98: add const + */ + +void I_Error(const char *error, ...) +{ + char errmsg[MAX_MESSAGE_SIZE]; + va_list argptr; + va_start(argptr,error); + doom_vsnprintf(errmsg,sizeof(errmsg),error,argptr); + va_end(argptr); + lprintf(LO_ERROR, "%s\n", errmsg); +#ifdef _WIN32 + if (!M_CheckParm ("-nodraw") && !capturing_video) { + I_MessageBox(errmsg, PRB_MB_OK); + } +#endif + I_SafeExit(-1); +} + +// Attempt to compensate for lack of va_copy + +#ifndef va_copy +# ifdef __va_copy +# define va_copy(a,b) __va_copy(a,b) +# else +# define va_copy(a,b) ((a)=(b)) +# endif +#endif + +// Wrapper to handle non-standard stdio implementations + +int doom_vsnprintf(char *buf, size_t max, const char *fmt, va_list va) +{ + int rv; + va_list vc; + + assert((max == 0 && buf == NULL) || (max != 0 && buf != NULL)); + assert(fmt != NULL); + + va_copy(vc, va); + rv = vsnprintf(buf, max, fmt, vc); + va_end(vc); + + if (rv < 0) // Handle an unhelpful return value. + { + // write into a scratch buffer that keeps growing until the output fits + static char *backbuffer; + static size_t backsize = 1024; + + for (; rv < 0; backsize *= 2) + { + if (backsize <= max) continue; + + backbuffer = (realloc)(backbuffer, backsize); + assert(backbuffer != NULL); + + va_copy(vc, va); + rv = vsnprintf(backbuffer, backsize, fmt, vc); + va_end(vc); + } + + if (buf) + { + size_t end = (size_t) rv >= max ? max-1 : rv; + memmove(buf, backbuffer, end); + buf[end] = '\0'; + } + } + + if (buf && (size_t) rv >= max && buf[max-1]) // ensure null-termination + buf[max-1] = '\0'; + + return rv; +} + +int doom_snprintf(char *buf, size_t max, const char *fmt, ...) +{ + int rv; + va_list va; + + va_start(va, fmt); + rv = doom_vsnprintf(buf, max, fmt, va); + va_end(va); + + return rv; +} diff --git a/src/lprintf.h b/src/lprintf.h new file mode 100644 index 0000000..e85b483 --- /dev/null +++ b/src/lprintf.h @@ -0,0 +1,65 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Declarations etc. for logical console output + * + *-----------------------------------------------------------------------------*/ + +#ifndef __LPRINTF__ +#define __LPRINTF__ + +#include + +typedef enum /* Logical output levels */ +{ + LO_INFO=1, /* One of these is used in each physical output */ + LO_CONFIRM=2, /* call. Which are output, or echoed to console */ + LO_WARN=4, /* if output redirected is determined by the */ + LO_ERROR=8, /* global masks: cons_output_mask,cons_error_mask. */ + LO_FATAL=16, + LO_DEBUG=32, + LO_ALWAYS=64, +} OutputLevels; + +#ifndef __GNUC__ +#define __attribute__(x) +#endif + +extern int lprintf(OutputLevels pri, const char *fmt, ...) __attribute__((format(printf,2,3))); +extern int cons_output_mask; +extern int cons_error_mask; + +/* killough 3/20/98: add const + * killough 4/25/98: add gcc attributes + * cphipps 01/11- moved from i_system.h */ +void I_Error(const char *error, ...) __attribute__((format(printf,1,2))); + +int doom_vsnprintf(char *buf, size_t max, const char *fmt, va_list va); +int doom_snprintf(char *buf, size_t max, const char *fmt, ...) __attribute__((format(printf,3,4))); +#endif diff --git a/src/m_argv.c b/src/m_argv.c new file mode 100644 index 0000000..816d642 --- /dev/null +++ b/src/m_argv.c @@ -0,0 +1,192 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Some argument handling. + * + *-----------------------------------------------------------------------------*/ + +#include +// CPhipps - include the correct header +#include "doomtype.h" +#include "z_zone.h" +#include "m_argv.h" + +int myargc; +char **myargv; + +// +// M_CheckParm +// Checks for the given parameter +// in the program's command line arguments. +// Returns the argument number (1 to argc-1) +// or 0 if not present +// + +int M_CheckParm(const char *check) +{ + signed int i = myargc; + while (--i>0) + if (!strcasecmp(check, myargv[i])) + return i; + return 0; +} + +// +// M_CheckParmEx +// Checks for the given parameter in the given params list. +// Returns the argument number (0 to paramscount-1) or -1 if not present +// + +int M_CheckParmEx(const char *check, char **params, int paramscount) +{ + if (paramscount > 0 && check && params && *params) + { + while (--paramscount >= 0) + { + if (!strcasecmp(check, params[paramscount])) + { + return paramscount; + } + } + } + + return -1; +} + +// +// Add one parameter to myargv list +// + +void M_AddParam(const char *param) +{ + myargv = realloc(myargv, sizeof(myargv[0]) * (myargc + 1)); + myargv[myargc] = strdup(param); + myargc++; +} + +// +// M_ParseCmdLine +// +// Parses the command line and sets up the argv[] array. +// On entry, cmdstart should point to the command line, +// argv should point to memory for the argv array, args +// points to memory to place the text of the arguments. +// If these are NULL, then no storing (only counting) +// is done. On exit, *numargs has the number of +// arguments (plus one for a final NULL argument), +// and *numchars has the number of bytes used in the buffer +// pointed to by args. +// +// Entry: +// char *cmdstart - pointer to command line +// char **argv - where to build argv array; NULL means don't build array +// char *args - where to place argument text; NULL means don't store text +// +// Exit: +// no return value +// int *numargs - returns number of argv entries created +// int *numchars - number of characters used in args buffer +// + +void M_ParseCmdLine(char *cmdstart, char **argv, char *args, int *numargs, int *numchars) +{ +#define IsSpace(c) ((c) == 0x20 || ((c) >= 0x09 && (c) <= 0x0D)) + + char *p; + int inquote; /* 1 = inside quotes */ + int copychar; /* 1 = copy char to *args */ + unsigned numslash; /* num of backslashes seen */ + + *numchars = 0; + *numargs = 0; + + p = cmdstart; + + inquote = 0; + + /* loop on each argument */ + for(;;) { + + while (IsSpace((int)*p)) ++p; + + if (*p == '\0')break; /* end of args */ + + /* scan an argument */ + if (argv) *argv++ = args; /* store ptr to arg */ + ++*numargs; + + /* loop through scanning one argument */ + for (;;) { + copychar = 1; + /* Rules: 2N backslashes + " ==> N backslashes and begin/end quote + 2N+1 backslashes + " ==> N backslashes + literal " + N backslashes ==> N backslashes */ + numslash = 0; + while (*p == '\\') { /* count number of backslashes for use below */ + ++p; + ++numslash; + } + if (*p == '\"') { + /* if 2N backslashes before, start/end quote, otherwise + copy literally */ + if ((numslash & 1) == 0) { + if (inquote) { + if (p[1] == '\"') + p++; /* Double quote inside quoted string */ + else /* skip first quote char and copy second */ + copychar = 0; + } else copychar = 0; /* don't copy quote */ + inquote = !inquote; + } + numslash >>= 1; /* divide numslash by two */ + } + + /* copy slashes */ + while (numslash--) { + if (args) *args++ = '\\'; + ++*numchars; + } + + /* if at end of arg, break loop */ + if (*p == '\0' || (!inquote && IsSpace((int)*p))) break; + + /* copy character into argument */ + if (copychar) { + if (args) *args++ = *p; + ++*numchars; + } + ++p; + } + + /* null-terminate the argument */ + + if (args) *args++ = '\0'; /* terminate string */ + ++*numchars; + } +} diff --git a/src/m_argv.h b/src/m_argv.h new file mode 100644 index 0000000..7ad5b10 --- /dev/null +++ b/src/m_argv.h @@ -0,0 +1,56 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Argument handling. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __M_ARGV__ +#define __M_ARGV__ + +/* + * MISC + */ +extern int myargc; +extern char **myargv; + +/* Returns the position of the given parameter in the arg list (0 if not found). */ +int M_CheckParm(const char *check); + +/* Returns the position of the given parameter in the params list (-1 if not found). */ +int M_CheckParmEx(const char *check, char **params, int paramscount); + +/* Add one parameter to myargv list */ +void M_AddParam(const char *param); + +/* Parses the command line and sets up the argv[] array */ +void M_ParseCmdLine(char *cmdstart, char **argv, char *args, int *numargs, int *numchars); + +#endif diff --git a/src/m_bbox.c b/src/m_bbox.c new file mode 100644 index 0000000..b0a2daa --- /dev/null +++ b/src/m_bbox.c @@ -0,0 +1,58 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Main loop menu stuff. + * Random number LUT. + * Default Config File. + * PCX Screenshots. + * + *-----------------------------------------------------------------------------*/ + +#ifdef __GNUG__ +#pragma implementation "m_bbox.h" +#endif +#include "m_bbox.h" + +void M_ClearBox (fixed_t *box) +{ + box[BOXTOP] = box[BOXRIGHT] = INT_MIN; + box[BOXBOTTOM] = box[BOXLEFT] = INT_MAX; +} + +void M_AddToBox(fixed_t* box,fixed_t x,fixed_t y) +{ + if (xbox[BOXRIGHT]) + box[BOXRIGHT] = x; + if (ybox[BOXTOP]) + box[BOXTOP] = y; +} diff --git a/src/m_bbox.h b/src/m_bbox.h new file mode 100644 index 0000000..5c2f2df --- /dev/null +++ b/src/m_bbox.h @@ -0,0 +1,56 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Simple bounding box datatype and functions. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __M_BBOX__ +#define __M_BBOX__ + +#include +#include "m_fixed.h" + +/* Bounding box coordinate storage. */ +enum +{ + BOXTOP, + BOXBOTTOM, + BOXLEFT, + BOXRIGHT +}; /* bbox coordinates */ + +/* Bounding box functions. */ + +void M_ClearBox(fixed_t* box); + +void M_AddToBox(fixed_t* box,fixed_t x,fixed_t y); + +#endif diff --git a/src/m_cheat.c b/src/m_cheat.c new file mode 100644 index 0000000..e6671f9 --- /dev/null +++ b/src/m_cheat.c @@ -0,0 +1,924 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Cheat sequence checking. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "g_game.h" +#include "r_data.h" +#include "p_inter.h" +#include "p_tick.h" +#include "m_cheat.h" +#include "m_argv.h" +#include "s_sound.h" +#include "sounds.h" +#include "dstrings.h" +#include "r_main.h" +#include "p_map.h" +#include "d_deh.h" // Ty 03/27/98 - externalized strings +/* cph 2006/07/23 - needs direct access to thinkercap */ +#include "p_tick.h" +#include "e6y.h" // G_GotoNextLevel() +#include "w_wad.h" // W_GetLumpInfoByNum() + +#define plyr (players+consoleplayer) /* the console player */ + +//e6y: for speedup +static int boom_cheat_route[MAX_COMPATIBILITY_LEVEL]; + +//----------------------------------------------------------------------------- +// +// CHEAT SEQUENCE PACKAGE +// +//----------------------------------------------------------------------------- + +static void cheat_mus(); +static void cheat_choppers(); +static void cheat_god(); +static void cheat_fa(); +static void cheat_k(); +static void cheat_kfa(); +static void cheat_noclip(); +static void cheat_pw(); +static void cheat_behold(); +static void cheat_clev(); +static void cheat_clev0(); +static void cheat_mypos(); +static void cheat_rate(); +static void cheat_comp(); +static void cheat_friction(); +static void cheat_pushers(); +static void cheat_tnttran(); +static void cheat_massacre(); +static void cheat_ddt(); +static void cheat_hom(); +static void cheat_fast(); +static void cheat_tntkey(); +static void cheat_tntkeyx(); +static void cheat_tntkeyxx(); +static void cheat_tntweap(); +static void cheat_tntweapx(); +static void cheat_tntammo(); +static void cheat_tntammox(); +static void cheat_smart(); +static void cheat_pitch(); +static void cheat_megaarmour(); +static void cheat_health(); +static void cheat_notarget(); +static void cheat_fly(); +static void cheat_skill(); +static void cheat_comp_ext(); +static void cheat_shorttics(); + +//----------------------------------------------------------------------------- +// +// List of cheat codes, functions, and special argument indicators. +// +// The first argument is the cheat code. +// +// The second argument is its DEH name, or NULL if it's not supported by -deh. +// +// The third argument is a combination of the bitmasks: +// {always, not_dm, not_coop, not_net, not_menu, not_demo, not_deh}, +// which excludes the cheat during certain modes of play. +// +// The fourth argument is the handler function. +// +// The fifth argument is passed to the handler function if it's non-negative; +// if negative, then its negative indicates the number of extra characters +// expected after the cheat code, which are passed to the handler function +// via a pointer to a buffer (after folding any letters to lowercase). +// +//----------------------------------------------------------------------------- + +cheatseq_t cheat[] = { + CHEAT("idmus", "Change music", always, cheat_mus, -2), + CHEAT("idchoppers", "Chainsaw", cht_never, cheat_choppers, 0), + CHEAT("iddqd", "God mode", cht_never, cheat_god, 0), + CHEAT("idkfa", "Ammo & Keys", cht_never, cheat_kfa, 0), + CHEAT("idfa", "Ammo", cht_never, cheat_fa, 0), + CHEAT("idspispopd", "No Clipping 1", cht_never, cheat_noclip, 0), + CHEAT("idclip", "No Clipping 2", cht_never, cheat_noclip, 0), + CHEAT("idbeholdh", "Invincibility", cht_never, cheat_health, 0), + CHEAT("idbeholdm", "Invincibility", cht_never, cheat_megaarmour, 0), + CHEAT("idbeholdv", "Invincibility", cht_never, cheat_pw, pw_invulnerability), + CHEAT("idbeholds", "Berserk", cht_never, cheat_pw, pw_strength), + CHEAT("idbeholdi", "Invisibility", cht_never, cheat_pw, pw_invisibility), + CHEAT("idbeholdr", "Radiation Suit", cht_never, cheat_pw, pw_ironfeet), + CHEAT("idbeholda", "Auto-map", not_dm, cheat_pw, pw_allmap), + CHEAT("idbeholdl", "Lite-Amp Goggles", not_dm, cheat_pw, pw_infrared), + CHEAT("idbehold", "BEHOLD menu", not_dm, cheat_behold, 0), + CHEAT("idclev", "Level Warp", cht_never | not_menu, cheat_clev, -2), + CHEAT("idclev", "Level Warp", cht_never | not_menu, cheat_clev0, 0), + CHEAT("idmypos", "Player Position", not_dm, cheat_mypos, 0), + CHEAT("idrate", "Frame rate", always, cheat_rate, 0), + // phares + CHEAT("tntcomp", NULL, cht_never, cheat_comp, 0), + // jff 2/01/98 kill all monsters + CHEAT("tntem", NULL, cht_never, cheat_massacre, 0), + // killough 2/07/98: moved from am_map.c + CHEAT("iddt", "Map cheat", not_dm, cheat_ddt, 0), + // killough 2/07/98: HOM autodetector + CHEAT("tnthom", NULL, always, cheat_hom, 0), + // killough 2/16/98: generalized key cheats + CHEAT("tntkey", NULL, cht_never, cheat_tntkey, 0), + CHEAT("tntkeyr", NULL, cht_never, cheat_tntkeyx, 0), + CHEAT("tntkeyy", NULL, cht_never, cheat_tntkeyx, 0), + CHEAT("tntkeyb", NULL, cht_never, cheat_tntkeyx, 0), + CHEAT("tntkeyrc", NULL, cht_never, cheat_tntkeyxx, it_redcard), + CHEAT("tntkeyyc", NULL, cht_never, cheat_tntkeyxx, it_yellowcard), + CHEAT("tntkeybc", NULL, cht_never, cheat_tntkeyxx, it_bluecard), + CHEAT("tntkeyrs", NULL, cht_never, cheat_tntkeyxx, it_redskull), + CHEAT("tntkeyys", NULL, cht_never, cheat_tntkeyxx, it_yellowskull), + // killough 2/16/98: end generalized keys + CHEAT("tntkeybs", NULL, cht_never, cheat_tntkeyxx, it_blueskull), + // Ty 04/11/98 - Added TNTKA + CHEAT("tntka", NULL, cht_never, cheat_k, 0), + // killough 2/16/98: generalized weapon cheats + CHEAT("tntweap", NULL, cht_never, cheat_tntweap, 0), + CHEAT("tntweap", NULL, cht_never, cheat_tntweapx, -1), + CHEAT("tntammo", NULL, cht_never, cheat_tntammo, 0), + // killough 2/16/98: end generalized weapons + CHEAT("tntammo", NULL, cht_never, cheat_tntammox, -1), + // invoke translucency // phares + CHEAT("tnttran", NULL, always, cheat_tnttran, 0), + // killough 2/21/98: smart monster toggle + CHEAT("tntsmart", NULL, cht_never, cheat_smart, 0), + // killough 2/21/98: pitched sound toggle + CHEAT("tntpitch", NULL, always, cheat_pitch, 0), + // killough 2/21/98: reduce RSI injury by adding simpler alias sequences: + // killough 2/21/98: same as tnttran + CHEAT("tntran", NULL, always, cheat_tnttran, 0), + // killough 2/21/98: same as tntammo + CHEAT("tntamo", NULL, cht_never, cheat_tntammo, 0), + // killough 2/21/98: same as tntammo + CHEAT("tntamo", NULL, cht_never, cheat_tntammox, -1), + // killough 3/6/98: -fast toggle + CHEAT("tntfast", NULL, cht_never, cheat_fast, 0), + // phares 3/10/98: toggle variable friction effects + CHEAT("tntice", NULL, cht_never, cheat_friction, 0), + // phares 3/10/98: toggle pushers + CHEAT("tntpush", NULL, cht_never, cheat_pushers, 0), + + // [RH] Monsters don't target + CHEAT("notarget", NULL, cht_never, cheat_notarget, 0), + // fly mode is active + CHEAT("fly", NULL, cht_never, cheat_fly, 0), + // Show skill level + CHEAT("skill", NULL, always, cheat_skill, 0), + + // Complevels with parameters + CHEAT("tntcl", NULL, cht_never, cheat_comp_ext, -2), + + // Enable/disable shorttics in-game + CHEAT("tntshort", NULL, cht_never, cheat_shorttics, 0), + + // end-of-list marker + {NULL} +}; + +//----------------------------------------------------------------------------- + +static void cheat_mus(buf) +char buf[3]; +{ + int musnum; + + //jff 3/20/98 note: this cheat allowed in netgame/demorecord + + //jff 3/17/98 avoid musnum being negative and crashing + if (!isdigit(buf[0]) || !isdigit(buf[1])) + return; + + plyr->message = s_STSTR_MUS; // Ty 03/27/98 - externalized + + if (gamemode == commercial) + { + musnum = mus_runnin + (buf[0]-'0')*10 + buf[1]-'0' - 1; + + //jff 4/11/98 prevent IDMUS00 in DOOMII and IDMUS36 or greater + if (musnum < mus_runnin || ((buf[0]-'0')*10 + buf[1]-'0') > 35) + plyr->message = s_STSTR_NOMUS; // Ty 03/27/98 - externalized + else + { + S_ChangeMusic(musnum, 1); + idmusnum = musnum; //jff 3/17/98 remember idmus number for restore + } + } + else + { + musnum = mus_e1m1 + (buf[0]-'1')*9 + (buf[1]-'1'); + + //jff 4/11/98 prevent IDMUS0x IDMUSx0 in DOOMI and greater than introa + if (buf[0] < '1' || buf[1] < '1' || ((buf[0]-'1')*9 + buf[1]-'1') > 31) + plyr->message = s_STSTR_NOMUS; // Ty 03/27/98 - externalized + else + { + S_ChangeMusic(musnum, 1); + idmusnum = musnum; //jff 3/17/98 remember idmus number for restore + } + } +} + +// 'choppers' invulnerability & chainsaw +static void cheat_choppers() +{ + plyr->weaponowned[wp_chainsaw] = true; + plyr->powers[pw_invulnerability] = true; + plyr->message = s_STSTR_CHOPPERS; // Ty 03/27/98 - externalized +} + +static void cheat_god() +{ // 'dqd' cheat for toggleable god mode + // dead players are first respawned at the current position + if (plyr->playerstate == PST_DEAD) + { + signed int an; + mapthing_t mt = {0}; + + P_MapStart(); + mt.x = plyr->mo->x >> FRACBITS; + mt.y = plyr->mo->y >> FRACBITS; + mt.angle = (plyr->mo->angle + ANG45/2)*(uint_64_t)45/ANG45; + mt.type = consoleplayer + 1; + mt.options = 1; // arbitrary non-zero value + P_SpawnPlayer(consoleplayer, &mt); + + // spawn a teleport fog + an = plyr->mo->angle >> ANGLETOFINESHIFT; + P_SpawnMobj(plyr->mo->x+20*finecosine[an], plyr->mo->y+20*finesine[an], plyr->mo->z, MT_TFOG); + S_StartSound(plyr, sfx_slop); + P_MapEnd(); + } + plyr->cheats ^= CF_GODMODE; + if (plyr->cheats & CF_GODMODE) + { + if (plyr->mo) + plyr->mo->health = god_health; // Ty 03/09/98 - deh + + plyr->health = god_health; + plyr->message = s_STSTR_DQDON; // Ty 03/27/98 - externalized + } + else + plyr->message = s_STSTR_DQDOFF; // Ty 03/27/98 - externalized +} + +// CPhipps - new health and armour cheat codes +static void cheat_health() +{ + if (!(plyr->cheats & CF_GODMODE)) { + if (plyr->mo) + plyr->mo->health = mega_health; + plyr->health = mega_health; + plyr->message = s_STSTR_BEHOLDX; // Ty 03/27/98 - externalized + } +} + +static void cheat_megaarmour() +{ + plyr->armorpoints = idfa_armor; // Ty 03/09/98 - deh + plyr->armortype = idfa_armor_class; // Ty 03/09/98 - deh + plyr->message = s_STSTR_BEHOLDX; // Ty 03/27/98 - externalized +} + +static void cheat_fa() +{ + int i; + + if (!plyr->backpack) + { + for (i=0 ; imaxammo[i] *= 2; + plyr->backpack = true; + } + + plyr->armorpoints = idfa_armor; // Ty 03/09/98 - deh + plyr->armortype = idfa_armor_class; // Ty 03/09/98 - deh + + // You can't own weapons that aren't in the game // phares 02/27/98 + for (i=0;iweaponowned[i] = true; + + for (i=0;iammo[i] = plyr->maxammo[i]; + + plyr->message = s_STSTR_FAADDED; +} + +static void cheat_k() +{ + int i; + for (i=0;icards[i]) // only print message if at least one key added + { // however, caller may overwrite message anyway + plyr->cards[i] = true; + plyr->message = "Keys Added"; + } +} + +static void cheat_kfa() +{ + cheat_k(); + cheat_fa(); + plyr->message = s_STSTR_KFAADDED; +} + +static void cheat_noclip() +{ + // Simplified, accepting both "noclip" and "idspispopd". + // no clipping mode cheat + + plyr->message = (plyr->cheats ^= CF_NOCLIP) & CF_NOCLIP ? + s_STSTR_NCON : s_STSTR_NCOFF; // Ty 03/27/98 - externalized +} + +// 'behold?' power-up cheats (modified for infinite duration -- killough) +static void cheat_pw(int pw) +{ + if (plyr->powers[pw]) + plyr->powers[pw] = pw!=pw_strength && pw!=pw_allmap; // killough + else + { + P_GivePower(plyr, pw); + if (pw != pw_strength) + plyr->powers[pw] = -1; // infinite duration -- killough + } + plyr->message = s_STSTR_BEHOLDX; // Ty 03/27/98 - externalized +} + +// 'behold' power-up menu +static void cheat_behold() +{ + plyr->message = s_STSTR_BEHOLD; // Ty 03/27/98 - externalized +} + +extern int EpiCustom; +struct MapEntry* G_LookupMapinfo(int gameepisode, int gamemap); + +// 'clev' change-level cheat +static void cheat_clev0() +{ + int epsd, map; + char *next; + + G_GotoNextLevel(&epsd, &map); + next = MAPNAME(epsd, map); + + if (W_CheckNumForName(next) != -1) + doom_printf("Current: %s, Next: %s", W_GetLumpInfoByNum(maplumpnum)->name, next); + else + doom_printf("Current: %s", W_GetLumpInfoByNum(maplumpnum)->name); +} + +static void cheat_clev(char buf[3]) +{ + int epsd, map; + struct MapEntry* entry; + + if (gamemode == commercial) + { + epsd = 1; //jff was 0, but espd is 1-based + map = (buf[0] - '0')*10 + buf[1] - '0'; + } + else + { + epsd = buf[0] - '0'; + map = buf[1] - '0'; + } + + // First check if we have a mapinfo entry for the requested level. If this is present the remaining checks should be skipped. + entry = G_LookupMapinfo(epsd, map); + if (!entry) + { + char *next; + + // Catch invalid maps. + if (epsd < 1 || map < 0 || // Ohmygod - this is not going to work. + ((gamemode == retail || gamemode == registered) && (epsd > 9 || map > 9)) || + (gamemode == shareware && (epsd > 1 || map > 9)) || + (gamemode == commercial && map > 99)) + return; + + if (gamemission == pack_nerve && map > 9) + return; + + // Chex.exe always warps to episode 1. + if (gamemission == chex) + { + epsd = 1; + } + + next = MAPNAME(epsd, map); + if (W_CheckNumForName(next) == -1) + { + doom_printf("IDCLEV target not found: %s", next); + return; + } + } + // So be it. + + plyr->message = s_STSTR_CLEV; // Ty 03/27/98 - externalized + + G_DeferedInitNew(gameskill, epsd, map); +} + +// 'mypos' for player position +// killough 2/7/98: simplified using dprintf and made output more user-friendly +static void cheat_mypos() +{ + doom_printf("Position (%d,%d,%d)\tAngle %-.0f", + players[consoleplayer].mo->x >> FRACBITS, + players[consoleplayer].mo->y >> FRACBITS, + players[consoleplayer].mo->z >> FRACBITS, + players[consoleplayer].mo->angle * (90.0/ANG90)); +} + +// cph - cheat to toggle frame rate/rendering stats display +static void cheat_rate() +{ + rendering_stats ^= 1; +} + +// compatibility cheat + +static void cheat_comp() +{ + // CPhipps - modified for new compatibility system + compatibility_level++; compatibility_level %= MAX_COMPATIBILITY_LEVEL; + // must call G_Compatibility after changing compatibility_level + // (fixes sf bug number 1558738) + G_Compatibility(); + doom_printf("New compatibility level:\n%s (%d)", comp_lev_str[compatibility_level], compatibility_level); +} + +// variable friction cheat +static void cheat_friction() +{ + plyr->message = // Ty 03/27/98 - *not* externalized + (variable_friction = !variable_friction) ? "Variable Friction enabled" : + "Variable Friction disabled"; +} + + +// Pusher cheat +// phares 3/10/98 +static void cheat_pushers() +{ + plyr->message = // Ty 03/27/98 - *not* externalized + (allow_pushers = !allow_pushers) ? "Pushers enabled" : "Pushers disabled"; +} + +// translucency cheat +static void cheat_tnttran() +{ + plyr->message = // Ty 03/27/98 - *not* externalized + (general_translucency = !general_translucency) ? "Translucency enabled" : + "Translucency disabled"; + + // killough 3/1/98, 4/11/98: cache translucency map on a demand basis + if (general_translucency && !main_tranmap) + R_InitTranMap(0); +} + +static void cheat_massacre() // jff 2/01/98 kill all monsters +{ + // jff 02/01/98 'em' cheat - kill all monsters + // partially taken from Chi's .46 port + // + // killough 2/7/98: cleaned up code and changed to use dprintf; + // fixed lost soul bug (LSs left behind when PEs are killed) + + int killcount=0; + thinker_t *currentthinker = NULL; + extern void A_PainDie(mobj_t *); + + // killough 7/20/98: kill friendly monsters only if no others to kill + uint_64_t mask = MF_FRIEND; + P_MapStart(); + do + while ((currentthinker = P_NextThinker(currentthinker,th_all)) != NULL) + if (currentthinker->function == P_MobjThinker && + !(((mobj_t *) currentthinker)->flags & mask) && // killough 7/20/98 + (((mobj_t *) currentthinker)->flags & MF_COUNTKILL || + ((mobj_t *) currentthinker)->type == MT_SKULL)) + { // killough 3/6/98: kill even if PE is dead + if (((mobj_t *) currentthinker)->health > 0) + { + killcount++; + P_DamageMobj((mobj_t *)currentthinker, NULL, NULL, 10000); + } + if (((mobj_t *) currentthinker)->type == MT_PAIN) + { + A_PainDie((mobj_t *) currentthinker); // killough 2/8/98 + P_SetMobjState ((mobj_t *) currentthinker, S_PAIN_DIE6); + } + } + while (!killcount && mask ? mask=0, 1 : 0); // killough 7/20/98 + P_MapEnd(); + // killough 3/22/98: make more intelligent about plural + // Ty 03/27/98 - string(s) *not* externalized + doom_printf("%d Monster%s Killed", killcount, killcount==1 ? "" : "s"); +} + +// killough 2/7/98: move iddt cheat from am_map.c to here +// killough 3/26/98: emulate Doom better +static void cheat_ddt() +{ + extern int ddt_cheating; + if (automapmode & am_active) + ddt_cheating = (ddt_cheating+1) % 3; +} + +// killough 2/7/98: HOM autodetection +static void cheat_hom() +{ + plyr->message = (flashing_hom = !flashing_hom) ? "HOM Detection On" : + "HOM Detection Off"; +} + +// killough 3/6/98: -fast parameter toggle +static void cheat_fast() +{ + plyr->message = (fastparm = !fastparm) ? "Fast Monsters On" : + "Fast Monsters Off"; // Ty 03/27/98 - *not* externalized + G_SetFastParms(fastparm); // killough 4/10/98: set -fast parameter correctly +} + +// killough 2/16/98: keycard/skullkey cheat functions +static void cheat_tntkey() +{ + plyr->message = "Red, Yellow, Blue"; // Ty 03/27/98 - *not* externalized +} + +static void cheat_tntkeyx() +{ + plyr->message = "Card, Skull"; // Ty 03/27/98 - *not* externalized +} + +static void cheat_tntkeyxx(int key) +{ + plyr->message = (plyr->cards[key] = !plyr->cards[key]) ? + "Key Added" : "Key Removed"; // Ty 03/27/98 - *not* externalized +} + +// killough 2/16/98: generalized weapon cheats + +static void cheat_tntweap() +{ // Ty 03/27/98 - *not* externalized + plyr->message = gamemode==commercial ? // killough 2/28/98 + "Weapon number 1-9" : "Weapon number 1-8"; +} + +static void cheat_tntweapx(buf) +char buf[3]; +{ + int w = *buf - '1'; + + if ((w==wp_supershotgun && gamemode!=commercial) || // killough 2/28/98 + ((w==wp_bfg || w==wp_plasma) && gamemode==shareware)) + return; + + if (w==wp_fist) // make '1' apply beserker strength toggle + cheat_pw(pw_strength); + else + if (w >= 0 && w < NUMWEAPONS) { + if ((plyr->weaponowned[w] = !plyr->weaponowned[w])) + plyr->message = "Weapon Added"; // Ty 03/27/98 - *not* externalized + else + { + plyr->message = "Weapon Removed"; // Ty 03/27/98 - *not* externalized + if (w==plyr->readyweapon) // maybe switch if weapon removed + plyr->pendingweapon = P_SwitchWeapon(plyr); + } + } +} + +// killough 2/16/98: generalized ammo cheats +static void cheat_tntammo() +{ + plyr->message = "Ammo 1-4, Backpack"; // Ty 03/27/98 - *not* externalized +} + +static void cheat_tntammox(buf) +char buf[1]; +{ + int a = *buf - '1'; + if (*buf == 'b') // Ty 03/27/98 - strings *not* externalized + if ((plyr->backpack = !plyr->backpack)) + for (plyr->message = "Backpack Added", a=0 ; amaxammo[a] <<= 1; + else + for (plyr->message = "Backpack Removed", a=0 ; aammo[a] > (plyr->maxammo[a] >>= 1)) + plyr->ammo[a] = plyr->maxammo[a]; + } + else + if (a>=0 && amessage = (plyr->ammo[a] = !plyr->ammo[a]) ? + plyr->ammo[a] = plyr->maxammo[a], "Ammo Added" : "Ammo Removed"; + } +} + +static void cheat_smart() +{ + plyr->message = (monsters_remember = !monsters_remember) ? + "Smart Monsters Enabled" : "Smart Monsters Disabled"; +} + +static void cheat_pitch() +{ + plyr->message=(pitched_sounds = !pitched_sounds) ? "Pitch Effects Enabled" : + "Pitch Effects Disabled"; +} + +static void cheat_notarget() +{ + plyr->cheats ^= CF_NOTARGET; + if (plyr->cheats & CF_NOTARGET) + plyr->message = "Notarget Mode ON"; + else + plyr->message = "Notarget Mode OFF"; +} + +static void cheat_fly() +{ + if (plyr->mo != NULL) + { + plyr->cheats ^= CF_FLY; + if (plyr->cheats & CF_FLY) + { + plyr->mo->flags |= MF_NOGRAVITY; + plyr->mo->flags |= MF_FLY; + plyr->message = "Fly mode ON"; + } + else + { + plyr->mo->flags &= ~MF_NOGRAVITY; + plyr->mo->flags &= ~MF_FLY; + plyr->message = "Fly mode OFF"; + } + } +} + +static void cheat_skill() +{ + doom_printf("Skill: %d", gameskill+1); +} + +//----------------------------------------------------------------------------- +// 2/7/98: Cheat detection rewritten by Lee Killough, to avoid +// scrambling and to use a more general table-driven approach. +//----------------------------------------------------------------------------- + +static int M_FindCheats_Boom(int key) +{ + static uint_64_t sr; + static char argbuf[CHEAT_ARGS_MAX+1], *arg; + static int init, argsleft, cht; + int i, ret, matchedbefore; + + // If we are expecting arguments to a cheat + // (e.g. idclev), put them in the arg buffer + + if (argsleft) + { + *arg++ = tolower(key); // store key in arg buffer + if (!--argsleft) // if last key in arg list, + cheat[cht].func(argbuf); // process the arg buffer + return 1; // affirmative response + } + + key = tolower(key) - 'a'; + if (key < 0 || key >= 32) // ignore most non-alpha cheat letters + { + sr = 0; // clear shift register + return 0; + } + + if (!init) // initialize aux entries of table + { + init = 1; + for (i=0;cheat[i].cheat;i++) + { + uint_64_t c=0, m=0; + const char *p; + + for (p=cheat[i].cheat; *p; p++) + { + unsigned key = tolower(*p)-'a'; // convert to 0-31 + if (key >= 32) // ignore most non-alpha cheat letters + continue; + c = (c<<5) + key; // shift key into code + m = (m<<5) + 31; // shift 1's into mask + } + cheat[i].code = c; // code for this cheat key + cheat[i].mask = m; // mask for this cheat key + } + } + + sr = (sr<<5) + key; // shift this key into shift register + + for (matchedbefore = ret = i = 0; cheat[i].cheat; i++) + if ((sr & cheat[i].mask) == cheat[i].code && // if match found + !(cheat[i].when & not_dm && deathmatch) && // and if cheat allowed + !(cheat[i].when & not_coop && netgame && !deathmatch) && + !(cheat[i].when & not_demo && (demorecording || demoplayback)) && + !(cheat[i].when & not_menu && menuactive) && + !(cheat[i].when & not_deh && M_CheckParm("-deh"))) { + if (cheat[i].arg < 0) // if additional args are required + { + cht = i; // remember this cheat code + arg = argbuf; // point to start of arg buffer + argsleft = -cheat[i].arg; // number of args expected + ret = 1; // responder has eaten key + } + else + if (!matchedbefore) // allow only one cheat at a time + { + matchedbefore = ret = 1; // responder has eaten key + cheat[i].func(cheat[i].arg); // call cheat handler + } + } + return ret; +} + +// +// CHEAT SEQUENCE PACKAGE +// + +// +// Called in st_stuff module, which handles the input. +// Returns a 1 if the cheat was successful, 0 if failed. +// +static int M_FindCheats_Doom(int key) +{ + int rc = 0; + cheatseq_t* cht; + char char_key; + + char_key = (char)key; + + for (cht = cheat; cht->cheat; cht++) + { + if (!(cht->when & not_dm && deathmatch) && // and if cheat allowed + !(cht->when & not_coop && netgame && !deathmatch) && + !(cht->when & not_demo && (demorecording || demoplayback)) && + !(cht->when & not_menu && menuactive) && + !(cht->when & not_deh && M_CheckParm("-deh"))) + { + // if we make a short sequence on a cheat with parameters, this + // will not work in vanilla doom. behave the same. + + if (demo_compatibility || compatibility_level == lxdoom_1_compatibility) + { + if (cht->arg < 0 && cht->deh_sequence_len < cht->sequence_len) + continue; + } + + if (cht->chars_read < cht->deh_sequence_len) + { + // still reading characters from the cheat code + // and verifying. reset back to the beginning + // if a key is wrong + + if (char_key == cht->cheat[cht->chars_read]) + ++cht->chars_read; + else + cht->chars_read = 0; + + cht->param_chars_read = 0; + } + else if (cht->param_chars_read < -cht->arg) + { + // we have passed the end of the cheat sequence and are + // entering parameters now + + cht->parameter_buf[cht->param_chars_read] = char_key; + + ++cht->param_chars_read; + + // affirmative response + rc = 1; + } + + if (cht->chars_read >= cht->deh_sequence_len && + cht->param_chars_read >= -cht->arg) + { + if (cht->param_chars_read) + { + static char argbuf[CHEAT_ARGS_MAX + 1]; + + // process the arg buffer + memcpy(argbuf, cht->parameter_buf, -cht->arg); + + cht->func(argbuf); + } + else + { + // call cheat handler + cht->func(cht->arg); + } + + cht->chars_read = cht->param_chars_read = 0; + rc = 1; + } + } + } + + return rc; +} + +static void cht_InitCheats(void) +{ + static int init = false; + + if (!init) + { + cheatseq_t* cht; + + init = true; + + memset(boom_cheat_route, 0, sizeof(boom_cheat_route)); + boom_cheat_route[boom_compatibility_compatibility] = 1; + boom_cheat_route[boom_201_compatibility] = 1; + boom_cheat_route[boom_202_compatibility] = 1; + boom_cheat_route[mbf_compatibility] = 1; + + for (cht = cheat; cht->cheat; cht++) + { + cht->deh_sequence_len = strlen(cht->cheat); + } + } +} + +dboolean M_FindCheats(int key) +{ + cht_InitCheats(); + + if (boom_cheat_route[compatibility_level]) + return M_FindCheats_Boom(key); + else + return M_FindCheats_Doom(key); +} + +// Extended compatibility cheat +static void cheat_comp_ext(char buf[3]) +{ + int cl = atoi(buf); + if(cl == 0 && (buf[0] != '0' || buf[1] != '0')) { + return; + } + if( cl < 0 || cl >= MAX_COMPATIBILITY_LEVEL ) { + return; + } + compatibility_level = cl; + G_Compatibility(); + doom_printf("New compatibility level:\n%s (%d)", comp_lev_str[compatibility_level], compatibility_level); +} + +// Enable/disable shorttics in-game +static void cheat_shorttics() +{ + shorttics = !shorttics; + if (shorttics) { + angle_t angle = plyr->mo->angle; + plyr->mo->angle = (angle >> 24) << 24; + doom_printf("Shorttics enabled"); + } else { + doom_printf("Shorttics disabled"); + } +} diff --git a/src/m_cheat.h b/src/m_cheat.h new file mode 100644 index 0000000..f960905 --- /dev/null +++ b/src/m_cheat.h @@ -0,0 +1,76 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Cheat code checking. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __M_CHEAT__ +#define __M_CHEAT__ + +#define CHEAT(cheat, deh_cheat, when, func, arg) \ + { cheat, deh_cheat, when, func, arg, 0, 0, \ + sizeof(cheat) - 1, 0, 0, 0, "" } + +#define CHEAT_ARGS_MAX 8 /* Maximum number of args at end of cheats */ + +/* killough 4/16/98: Cheat table structure */ + +typedef struct cheatseq_s { + const char * cheat; + const char *const deh_cheat; + enum { + always = 0, + not_dm = 1, + not_coop = 2, + not_demo = 4, + not_menu = 8, + not_deh = 16, + not_net = not_dm | not_coop, + cht_never = not_net | not_demo + } const when; + void (*const func)(); + const int arg; + uint_64_t code, mask; + + // settings for this cheat + size_t sequence_len; + size_t deh_sequence_len; + + // state used during the game + size_t chars_read; + int param_chars_read; + char parameter_buf[CHEAT_ARGS_MAX]; +} cheatseq_t; + +extern cheatseq_t cheat[]; + +dboolean M_FindCheats(int key); + +#endif diff --git a/src/m_fixed.h b/src/m_fixed.h new file mode 100644 index 0000000..72052f5 --- /dev/null +++ b/src/m_fixed.h @@ -0,0 +1,124 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Fixed point arithemtics, implementation. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __M_FIXED__ +#define __M_FIXED__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "doomtype.h" + +/* + * Fixed point, 32bit as 16.16. + */ + +#define FRACBITS 16 +#define FRACUNIT (1<> (8*sizeof _t-1); + return (_t^_s)-_s; +} +#else +#define D_abs abs +#endif + +/* + * Fixed Point Multiplication + */ + + +/* CPhipps - made __inline__ to inline, as specified in the gcc docs + * Also made const */ + +inline static CONSTFUNC fixed_t FixedMul(fixed_t a, fixed_t b) +{ + return (fixed_t)((int_64_t) a*b >> FRACBITS); +} + +/* + * Fixed Point Division + */ + +static CONSTFUNC fixed_t FixedDiv(fixed_t a, fixed_t b) +{ + return (D_abs(a)>>14) >= D_abs(b) ? ((a^b)>>31) ^ INT_MAX : + (fixed_t)(((int_64_t) a << FRACBITS) / b); +} + +/* CPhipps - + * FixedMod - returns a % b, guaranteeing 0<=a +#include +#include +#include + +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + #include + #include + #include +#else + #include + #include + #include +#endif + +#include + +#include "doomtype.h" +#include "lprintf.h" + +#ifdef _WIN32 +static wchar_t *ConvertMultiByteToWide(const char *str, UINT code_page) +{ + wchar_t *wstr = NULL; + int wlen = 0; + + wlen = MultiByteToWideChar(code_page, 0, str, -1, NULL, 0); + + if (!wlen) + { + errno = EINVAL; + lprintf(LO_INFO, "Warning: Failed to convert path to wide encoding\n"); + return NULL; + } + + wstr = malloc(sizeof(wchar_t) * wlen); + + if (!wstr) + { + lprintf(LO_INFO, "ConvertMultiByteToWide: Failed to allocate new string\n"); + return NULL; + } + + if (MultiByteToWideChar(code_page, 0, str, -1, wstr, wlen) == 0) + { + errno = EINVAL; + lprintf(LO_INFO, "Warning: Failed to convert path to wide encoding\n"); + free(wstr); + return NULL; + } + + return wstr; +} + +static char *ConvertWideToMultiByte(const wchar_t *wstr, UINT code_page) +{ + char *str = NULL; + int len = 0; + + len = WideCharToMultiByte(code_page, 0, wstr, -1, NULL, 0, NULL, NULL); + + if (!len) + { + errno = EINVAL; + lprintf(LO_INFO, "Warning: Failed to convert path to multi byte encoding\n"); + return NULL; + } + + str = malloc(sizeof(char) * len); + + if (!str) + { + lprintf(LO_INFO, "ConvertWideToMultiByte: Failed to allocate new string\n"); + return NULL; + } + + if (WideCharToMultiByte(code_page, 0, wstr, -1, str, len, NULL, NULL) == 0) + { + errno = EINVAL; + lprintf(LO_INFO, "Warning: Failed to convert path to multi byte encoding\n"); + free(str); + return NULL; + } + + return str; +} + +static wchar_t *ConvertUtf8ToWide(const char *str) +{ + return ConvertMultiByteToWide(str, CP_UTF8); +} + +static char *ConvertWideToUtf8(const wchar_t *wstr) +{ + return ConvertWideToMultiByte(wstr, CP_UTF8); +} + +static wchar_t *ConvertSysNativeMBToWide(const char *str) +{ + return ConvertMultiByteToWide(str, CP_ACP); +} + +static char *ConvertWideToSysNativeMB(const wchar_t *wstr) +{ + return ConvertWideToMultiByte(wstr, CP_ACP); +} +#endif + +char *M_ConvertSysNativeMBToUtf8(const char *str) +{ +#ifdef _WIN32 + char *ret = NULL; + wchar_t *wstr = NULL; + + wstr = ConvertSysNativeMBToWide(str); + + if (!wstr) + { + return NULL; + } + + ret = ConvertWideToUtf8(wstr); + + free(wstr); + + return ret; +#else + return strdup(str); +#endif +} + +char *M_ConvertUtf8ToSysNativeMB(const char *str) +{ +#ifdef _WIN32 + char *ret = NULL; + wchar_t *wstr = NULL; + + wstr = ConvertUtf8ToWide(str); + + if (!wstr) + { + return NULL; + } + + ret = ConvertWideToSysNativeMB(wstr); + + free(wstr); + + return ret; +#else + return strdup(str); +#endif +} + +FILE* M_fopen(const char *filename, const char *mode) +{ +#ifdef _WIN32 + FILE *file; + wchar_t *wname = NULL; + wchar_t *wmode = NULL; + + wname = ConvertUtf8ToWide(filename); + + if (!wname) + { + return NULL; + } + + wmode = ConvertUtf8ToWide(mode); + + if (!wmode) + { + free(wname); + return NULL; + } + + file = _wfopen(wname, wmode); + + free(wname); + free(wmode); + + return file; +#else + return fopen(filename, mode); +#endif +} + +int M_remove(const char *path) +{ +#ifdef _WIN32 + wchar_t *wpath = NULL; + int ret; + + wpath = ConvertUtf8ToWide(path); + + if (!wpath) + { + return 0; + } + + ret = _wremove(wpath); + + free(wpath); + + return ret; +#else + return remove(path); +#endif +} + +int M_stat(const char *path, struct stat *buf) +{ +#ifdef _WIN32 + wchar_t *wpath = NULL; + struct _stat wbuf; + int ret; + + wpath = ConvertUtf8ToWide(path); + + if (!wpath) + { + return -1; + } + + ret = _wstat(wpath, &wbuf); + + // The _wstat() function expects a struct _stat* parameter that is + // incompatible with struct stat*. We copy only the required compatible + // field. + buf->st_mode = wbuf.st_mode; + buf->st_mtime = wbuf.st_mtime; + + free(wpath); + + return ret; +#else + return stat(path, buf); +#endif +} + +int M_open(const char *filename, int oflag) +{ +#ifdef _WIN32 + wchar_t *wname = NULL; + int ret; + + wname = ConvertUtf8ToWide(filename); + + if (!wname) + { + return 0; + } + + ret = _wopen(wname, oflag); + + free(wname); + + return ret; +#else + return open(filename, oflag); +#endif +} + +int M_access(const char *path, int mode) +{ +#ifdef _WIN32 + wchar_t *wpath = NULL; + int ret; + + wpath = ConvertUtf8ToWide(path); + + if (!wpath) + { + return 0; + } + + ret = _waccess(wpath, mode); + + free(wpath); + + return ret; +#else + return access(path, mode); +#endif +} + +char *M_getcwd(char *buffer, int len) +{ +#ifdef _WIN32 + wchar_t *wret; + char *ret; + + wret = _wgetcwd(NULL, 0); + + if (!wret) + { + return NULL; + } + + ret = ConvertWideToUtf8(wret); + + free(wret); + + if (!ret) + { + return NULL; + } + + if (buffer) + { + if (strlen(ret) >= len) + { + free(ret); + return NULL; + } + + strcpy(buffer, ret); + free(ret); + + return buffer; + } + else + { + return ret; + } +#else + return getcwd(buffer, len); +#endif +} + +int M_mkdir(const char *path) +{ +#ifdef _WIN32 + wchar_t *wdir = NULL; + int ret; + + wdir = ConvertUtf8ToWide(path); + + if (!wdir) + { + return -1; + } + + ret = _wmkdir(wdir); + + free(wdir); + + return ret; +#else + return mkdir(path, 0755); +#endif +} + +#ifdef _WIN32 +typedef struct { + char *var; + const char *name; +} env_var_t; + +static env_var_t *env_vars; +static int num_vars; +#endif + +char *M_getenv(const char *name) +{ +#ifdef _WIN32 + int i; + wchar_t *wenv = NULL, *wname = NULL; + char *env; + + for (i = 0; i < num_vars; ++i) + { + if (!strcasecmp(name, env_vars[i].name)) + return env_vars[i].var; + } + + wname = ConvertUtf8ToWide(name); + + if (!wname) + { + return NULL; + } + + wenv = _wgetenv(wname); + + free(wname); + + if (wenv) + { + env = ConvertWideToUtf8(wenv); + } + else + { + env = NULL; + } + + env_vars = realloc(env_vars, (num_vars + 1) * sizeof(*env_vars)); + env_vars[num_vars].var = env; + env_vars[num_vars].name = strdup(name); + ++num_vars; + + return env; +#else + return getenv(name); +#endif +} diff --git a/src/m_io.h b/src/m_io.h new file mode 100644 index 0000000..fd1ef85 --- /dev/null +++ b/src/m_io.h @@ -0,0 +1,35 @@ +// Copyright (C) 2022 Roman Fomin +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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: +// Compatibility wrappers from Chocolate Doom +// + +#ifndef M_IO_INCLUDED +#define M_IO_INCLUDED + +#include +#include + +FILE *M_fopen(const char *filename, const char *mode); +int M_remove(const char *path); +int M_stat(const char *path, struct stat *buf); +int M_open(const char *filename, int oflag); +int M_access(const char *path, int mode); +char *M_getcwd(char *buffer, int len); +int M_mkdir(const char *dir); +char *M_getenv(const char *name); + +char *M_ConvertSysNativeMBToUtf8(const char *str); +char *M_ConvertUtf8ToSysNativeMB(const char *str); + +#endif // M_IO_INCLUDED diff --git a/src/m_menu.c b/src/m_menu.c new file mode 100644 index 0000000..144a7d0 --- /dev/null +++ b/src/m_menu.c @@ -0,0 +1,6401 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * DOOM selection menu, options, episode etc. (aka Big Font menus) + * Sliders and icons. Kinda widget stuff. + * Setup Menus. + * Extended HELP screens. + * Dynamic HELP screen. + * + *-----------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "doomdef.h" +#include "doomstat.h" +#include "dstrings.h" +#include "d_main.h" +#include "v_video.h" +#include "w_wad.h" +#include "r_main.h" +#include "hu_stuff.h" +#include "st_stuff.h" +#include "g_game.h" +#include "s_sound.h" +#include "sounds.h" +#include "m_menu.h" +#include "d_deh.h" +#include "m_misc.h" +#include "lprintf.h" +#include "am_map.h" +#include "i_main.h" +#include "i_system.h" +#include "i_video.h" +#include "i_sound.h" +#include "r_demo.h" +#include "r_fps.h" +#include "e6y.h"//e6y +#include "m_io.h" +#ifdef _WIN32 +#include "e6y_launcher.h" +#endif + +extern patchnum_t hu_font[HU_FONTSIZE]; +extern dboolean message_dontfuckwithme; + +extern dboolean chat_on; // in heads-up code + +// +// defaulted values +// + +int mouseSensitivity_horiz; // has default // killough +int mouseSensitivity_vert; // has default + +int showMessages; // Show messages has default, 0 = off, 1 = on + +int hide_setup=1; // killough 5/15/98 + +// Blocky mode, has default, 0 = high, 1 = normal +//int detailLevel; obsolete -- killough +int screenblocks; // has default + +int screenSize; // temp for screenblocks (0-9) + +int quickSaveSlot; // -1 = no quicksave slot picked! + +int messageToPrint; // 1 = message to be printed + +// CPhipps - static const +static const char* messageString; // ...and here is the message string! + +// message x & y +int messx; +int messy; +int messageLastMenuActive; + +dboolean messageNeedsInput; // timed message = no input from user + +void (*messageRoutine)(int response); + +#define SAVESTRINGSIZE 24 + +/* killough 8/15/98: when changes are allowed to sync-critical variables */ +static int allow_changes(void) +{ + return !(demoplayback || demorecording || netgame); +} + +static void M_UpdateCurrent(default_t* def) +{ + /* cph - requires rewrite of m_misc.c */ + if (def->current) { + if (allow_changes()) /* killough 8/15/98 */ + *def->current = *def->location.pi; + else if (*def->current != *def->location.pi) + warn_about_changes(S_LEVWARN); /* killough 8/15/98 */ + } +} + +int warning_about_changes, print_warning_about_changes; + +/* cphipps - M_DrawBackground renamed and moved to v_video.c */ + +dboolean menu_background = 1; // do Boom fullscreen menus have backgrounds? + +static void M_DrawBackground(const char *flat, int scrn) +{ + if (menu_background) + V_DrawBackground(flat, scrn); +} + +// we are going to be entering a savegame string + +int saveStringEnter; +int saveSlot; // which slot to save in +int saveCharIndex; // which char we're editing +// old save description before edit +char saveOldString[SAVESTRINGSIZE]; + +dboolean inhelpscreens; // indicates we are in or just left a help screen + +dboolean BorderNeedRefresh; + +enum menuactive_e menuactive; // The menus are up + +#define SKULLXOFF -32 +#define LINEHEIGHT 16 + +char savegamestrings[10][SAVESTRINGSIZE]; + +// +// MENU TYPEDEFS +// + +typedef struct +{ + short status; // 0 = no cursor here, 1 = ok, 2 = arrows ok + char name[10]; + + // choice = menu item #. + // if status = 2, + // choice=0:leftarrow,1:rightarrow + void (*routine)(int choice); + char alphaKey; // hotkey in menu + const char *alttext; +} menuitem_t; + +typedef struct menu_s +{ + short numitems; // # of menu items + struct menu_s* prevMenu; // previous menu + menuitem_t* menuitems; // menu items + void (*routine)(); // draw routine + short x; + short y; // x,y of menu + short lastOn; // last item user was on in menu +} menu_t; + +short itemOn; // menu item skull is on (for Big Font menus) +short skullAnimCounter; // skull animation counter +short whichSkull; // which skull to draw (he blinks) + +// graphic name of skulls + +const char skullName[2][/*8*/9] = {"M_SKULL1","M_SKULL2"}; + +menu_t* currentMenu; // current menudef + +// phares 3/30/98 +// externs added for setup menus + +int mapcolor_me; // cph + +extern int map_point_coordinates; // killough 10/98 +extern int map_level_stat; + +extern char* chat_macros[]; // chat macros +extern const char* shiftxform; +extern default_t defaults[]; +extern int numdefaults; + +// end of externs added for setup menus + +// +// PROTOTYPES +// +void M_NewGame(int choice); +void M_Episode(int choice); +void M_ChooseSkill(int choice); +void M_LoadGame(int choice); +void M_SaveGame(int choice); +void M_Options(int choice); +void M_EndGame(int choice); +void M_ReadThis(int choice); +void M_ReadThis2(int choice); +void M_QuitDOOM(int choice); + +void M_ChangeMessages(int choice); +void M_ChangeSensitivity(int choice); +void M_SfxVol(int choice); +void M_MusicVol(int choice); +/* void M_ChangeDetail(int choice); unused -- killough */ +void M_SizeDisplay(int choice); +void M_StartGame(int choice); +void M_Sound(int choice); + +void M_Mouse(int choice, int *sens); /* killough */ +void M_MouseVert(int choice); +void M_MouseHoriz(int choice); +void M_DrawMouse(void); + +void M_FinishReadThis(int choice); +void M_FinishHelp(int choice); // killough 10/98 +void M_LoadSelect(int choice); +void M_SaveSelect(int choice); +void M_ReadSaveStrings(void); +void M_QuickSave(void); +void M_QuickLoad(void); + +void M_DrawMainMenu(void); +void M_DrawReadThis1(void); +void M_DrawReadThis2(void); +void M_DrawNewGame(void); +void M_DrawEpisode(void); +void M_DrawOptions(void); +void M_DrawSound(void); +void M_DrawLoad(void); +void M_DrawSave(void); +void M_DrawSetup(void); // phares 3/21/98 +void M_DrawHelp (void); // phares 5/04/98 + +void M_DrawSaveLoadBorder(int x,int y); +void M_SetupNextMenu(menu_t *menudef); +void M_DrawThermo(int x,int y,int thermWidth,int thermDot); +void M_DrawEmptyCell(menu_t *menu,int item); +void M_DrawSelCell(menu_t *menu,int item); +void M_WriteText(int x, int y, const char *string, int cm); +int M_StringWidth(const char *string); +int M_StringHeight(const char *string); +void M_DrawTitle(int x, int y, const char *patch, int cm, + const char *alttext, int altcm); +void M_StartMessage(const char *string,void *routine,dboolean input); +void M_StopMessage(void); +void M_ClearMenus (void); + +// phares 3/30/98 +// prototypes added to support Setup Menus and Extended HELP screens + +int M_GetKeyString(int,int); +void M_Setup(int choice); +void M_KeyBindings(int choice); +void M_Weapons(int); +void M_StatusBar(int); +void M_Automap(int); +void M_Enemy(int); +void M_Messages(int); +void M_ChatStrings(int); +void M_InitExtendedHelp(void); +void M_ExtHelpNextScreen(int); +void M_ExtHelp(int); +static int M_GetPixelWidth(const char*); +void M_DrawKeybnd(void); +void M_DrawWeapons(void); +static void M_DrawString(int cx, int cy, int color, const char* ch); +static void M_DrawMenuString(int,int,int); +static void M_DrawStringCentered(int,int,int,const char*); +void M_DrawStatusHUD(void); +void M_DrawExtHelp(void); +void M_DrawAutoMap(void); +void M_DrawEnemy(void); +void M_DrawMessages(void); +void M_DrawChatStrings(void); +void M_Compat(int); // killough 10/98 +void M_ChangeDemoSmoothTurns(void); +void M_ChangeTextureParams(void); +void M_General(int); // killough 10/98 +void M_DrawCompat(void); // killough 10/98 +void M_DrawGeneral(void); // killough 10/98 +void M_ChangeFullScreen(void); +void M_ChangeVideoMode(void); +void M_ChangeUseGLSurface(void); +void M_ChangeApplyPalette(void); + +menu_t NewDef; // phares 5/04/98 + +// end of prototypes added to support Setup Menus and Extended HELP screens + +///////////////////////////////////////////////////////////////////////////// +// +// DOOM MENUS +// + +///////////////////////////// +// +// MAIN MENU +// + +// main_e provides numerical values for which Big Font screen you're on + +enum +{ + newgame = 0, + loadgame, + savegame, + options, + readthis, + quitdoom, + main_end +} main_e; + +// +// MainMenu is the definition of what the main menu Screen should look +// like. Each entry shows that the cursor can land on each item (1), the +// built-in graphic lump (i.e. "M_NGAME") that should be displayed, +// the program which takes over when an item is selected, and the hotkey +// associated with the item. +// + +menuitem_t MainMenu[]= +{ + {1,"M_NGAME", M_NewGame, 'n'}, + {1,"M_OPTION",M_Options, 'o'}, + {1,"M_LOADG", M_LoadGame,'l'}, + {1,"M_SAVEG", M_SaveGame,'s'}, + // Another hickup with Special edition. + {1,"M_RDTHIS",M_ReadThis,'r'}, + {1,"M_QUITG", M_QuitDOOM,'q'} +}; + +menu_t MainDef = +{ + main_end, // number of menu items + NULL, // previous menu screen + MainMenu, // table that defines menu items + M_DrawMainMenu, // drawing routine + 97,64, // initial cursor position + 0 // last menu item the user was on +}; + +// +// M_DrawMainMenu +// + +void M_DrawMainMenu(void) +{ + // CPhipps - patch drawing updated + V_DrawNamePatch(94, 2, 0, "M_DOOM", CR_DEFAULT, VPT_STRETCH); +} + +///////////////////////////// +// +// Read This! MENU 1 & 2 +// + +// There are no menu items on the Read This! screens, so read_e just +// provides a placeholder to maintain structure. + +enum +{ + rdthsempty1, + read1_end +} read_e; + +enum +{ + rdthsempty2, + read2_end +} read_e2; + +enum // killough 10/98 +{ + helpempty, + help_end +} help_e; + + +// The definitions of the Read This! screens + +menuitem_t ReadMenu1[] = +{ + {1,"",M_ReadThis2,0} +}; + +menuitem_t ReadMenu2[]= +{ + {1,"",M_FinishReadThis,0} +}; + +menuitem_t HelpMenu[]= // killough 10/98 +{ + {1,"",M_FinishHelp,0} +}; + +menu_t ReadDef1 = +{ + read1_end, + &MainDef, + ReadMenu1, + M_DrawReadThis1, + 330,175, + //280,185, // killough 2/21/98: fix help screens + 0 +}; + +menu_t ReadDef2 = +{ + read2_end, + &ReadDef1, + ReadMenu2, + M_DrawReadThis2, + 330,175, + 0 +}; + +menu_t HelpDef = // killough 10/98 +{ + help_end, + &HelpDef, + HelpMenu, + M_DrawHelp, + 330,175, + 0 +}; + +// +// M_ReadThis +// + +void M_ReadThis(int choice) +{ + M_SetupNextMenu(&ReadDef1); +} + +void M_ReadThis2(int choice) +{ + M_SetupNextMenu(&ReadDef2); +} + +void M_FinishReadThis(int choice) +{ + M_SetupNextMenu(&MainDef); +} + +void M_FinishHelp(int choice) // killough 10/98 +{ + M_SetupNextMenu(&MainDef); +} + +// +// Read This Menus +// Had a "quick hack to fix romero bug" +// +// killough 10/98: updated with new screens + +void M_DrawReadThis1(void) +{ + inhelpscreens = true; + if (gamemode == shareware) + { + // e6y: wide-res + V_FillBorder(-1, 0); + V_DrawNamePatch(0, 0, 0, "HELP2", CR_DEFAULT, VPT_STRETCH); + } + else + M_DrawCredits(); +} + +// +// Read This Menus - optional second page. +// +// killough 10/98: updated with new screens + +void M_DrawReadThis2(void) +{ + inhelpscreens = true; + if (gamemode == shareware) + M_DrawCredits(); + else + { + // e6y: wide-res + V_FillBorder(-1, 0); + V_DrawNamePatch(0, 0, 0, "CREDIT", CR_DEFAULT, VPT_STRETCH); + } +} + +///////////////////////////// +// +// EPISODE SELECT +// + +// +// episodes_e provides numbers for the episode menu items. The default is +// 4, to accomodate Ultimate Doom. If the user is running anything else, +// this is accounted for in the code. +// + +enum +{ + ep1, + ep2, + ep3, + ep4, + ep_end +} episodes_e; + +// The definitions of the Episodes menu + +menuitem_t EpisodeMenu[]= // added a few free entries for UMAPINFO +{ + {1,"M_EPI1", M_Episode,'k'}, + {1,"M_EPI2", M_Episode,'t'}, + {1,"M_EPI3", M_Episode,'i'}, + {1,"M_EPI4", M_Episode,'t'}, + {1,"", M_Episode,'0'}, + {1,"", M_Episode,'0'}, + {1,"", M_Episode,'0'}, + {1,"", M_Episode,'0'} +}; + +menu_t EpiDef = +{ + ep_end, // # of menu items + &MainDef, // previous menu + EpisodeMenu, // menuitem_t -> + M_DrawEpisode, // drawing routine -> + 48,63, // x,y + ep1 // lastOn +}; + +// This is for customized episode menus +int EpiCustom; +short EpiMenuMap[8] = { 1, 1, 1, 1, -1, -1, -1, -1 }, EpiMenuEpi[8] = { 1,2,3,4,-1,-1,-1,-1 }; + +// +// M_Episode +// +int epiChoice; + +void M_ClearEpisodes(void) +{ + EpiDef.numitems = 0; +} + +void M_AddEpisode(const char *map, const char *gfx, const char *txt, const char *alpha) +{ + if (!EpiCustom) + { + EpiCustom = true; + NewDef.prevMenu = &EpiDef; + + if (gamemode == commercial || gamemission == chex) + EpiDef.numitems = 0; + } + + { + int epi, mapnum; + if (EpiDef.numitems >= 8) return; + G_ValidateMapName(map, &epi, &mapnum); + EpiMenuEpi[EpiDef.numitems] = epi; + EpiMenuMap[EpiDef.numitems] = mapnum; + strncpy(EpisodeMenu[EpiDef.numitems].name, gfx, 8); + EpisodeMenu[EpiDef.numitems].name[8] = 0; + EpisodeMenu[EpiDef.numitems].alttext = txt ? strdup(txt) : NULL; + EpisodeMenu[EpiDef.numitems].alphaKey = alpha ? *alpha : 0; + EpiDef.numitems++; + } + if (EpiDef.numitems <= 4) + { + EpiDef.y = 63; + } + else + { + EpiDef.y = 63 - (EpiDef.numitems - 4) * (LINEHEIGHT / 2); + } +} + +void M_DrawEpisode(void) +{ + // CPhipps - patch drawing updated + V_DrawNamePatch(54, EpiDef.y - 25, 0, "M_EPISOD", CR_DEFAULT, VPT_STRETCH); +} + +void M_Episode(int choice) +{ + if (!EpiCustom) + { + if ((gamemode == shareware) && choice) { + M_StartMessage(s_SWSTRING, NULL, false); // Ty 03/27/98 - externalized + M_SetupNextMenu(&ReadDef1); + return; + } + + // Yet another hack... + if ((gamemode == registered) && (choice > 2) && !EpiCustom) + { + lprintf(LO_WARN, + "M_Episode: 4th episode requires UltimateDOOM\n"); + choice = 0; + } + } + epiChoice = choice; + M_SetupNextMenu(&NewDef); +} + +///////////////////////////// +// +// NEW GAME +// + +// numerical values for the New Game menu items + +enum +{ + killthings, + toorough, + hurtme, + violence, + nightmare, + newg_end +} newgame_e; + +// The definitions of the New Game menu + +menuitem_t NewGameMenu[]= +{ + {1,"M_JKILL", M_ChooseSkill, 'i'}, + {1,"M_ROUGH", M_ChooseSkill, 'h'}, + {1,"M_HURT", M_ChooseSkill, 'h'}, + {1,"M_ULTRA", M_ChooseSkill, 'u'}, + {1,"M_NMARE", M_ChooseSkill, 'n'} +}; + +menu_t NewDef = +{ + newg_end, // # of menu items + &EpiDef, // previous menu + NewGameMenu, // menuitem_t -> + M_DrawNewGame, // drawing routine -> + 48,63, // x,y + hurtme // lastOn +}; + +// +// M_NewGame +// + +void M_DrawNewGame(void) +{ + // CPhipps - patch drawing updated + V_DrawNamePatch(96, 14, 0, "M_NEWG", CR_DEFAULT, VPT_STRETCH); + V_DrawNamePatch(54, 38, 0, "M_SKILL",CR_DEFAULT, VPT_STRETCH); +} + +/* cph - make `New Game' restart the level in a netgame */ +static void M_RestartLevelResponse(int ch) +{ + if (ch != 'y') + return; + + if (demorecording) + I_SafeExit(0); + + currentMenu->lastOn = itemOn; + M_ClearMenus (); + G_RestartLevel (); +} + +void M_NewGame(int choice) +{ + if (netgame && !demoplayback) { + if (compatibility_level < lxdoom_1_compatibility) + M_StartMessage(s_NEWGAME,NULL,false); // Ty 03/27/98 - externalized + else // CPhipps - query restarting the level + M_StartMessage(s_RESTARTLEVEL,M_RestartLevelResponse,true); + return; + } + + if (demorecording) { /* killough 5/26/98: exclude during demo recordings */ + M_StartMessage("you can't start a new game\n" + "while recording a demo!\n\n"PRESSKEY, + NULL, false); // killough 5/26/98: not externalized + return; + } + + // Chex Quest disabled the episode select screen, as did Doom II. + if (((gamemode == commercial || gamemission == chex) && !EpiCustom) || EpiDef.numitems == 1) + M_SetupNextMenu(&NewDef); + else + { + epiChoice = 0; + M_SetupNextMenu(&EpiDef); + } +} + +// CPhipps - static +static void M_VerifyNightmare(int ch) +{ + if (ch != 'y') + return; + + G_DeferedInitNew(nightmare,EpiMenuEpi[epiChoice], EpiMenuMap[epiChoice]); + M_ClearMenus (); +} + +void M_ChooseSkill(int choice) +{ + if (choice == nightmare) + { // Ty 03/27/98 - externalized + M_StartMessage(s_NIGHTMARE,M_VerifyNightmare,true); + return; + } + if (EpiMenuEpi[epiChoice] == -1 || EpiMenuMap[epiChoice] == -1) return; // There is no map to start here. + + G_DeferedInitNew(choice, EpiMenuEpi[epiChoice], EpiMenuMap[epiChoice]); + M_ClearMenus (); +} + +///////////////////////////// +// +// LOAD GAME MENU +// + +// numerical values for the Load Game slots + +enum +{ + load1, + load2, + load3, + load4, + load5, + load6, + load7, //jff 3/15/98 extend number of slots + load8, + load_end +} load_e; + +// The definitions of the Load Game screen + +menuitem_t LoadMenue[]= +{ + {1,"", M_LoadSelect,'1'}, + {1,"", M_LoadSelect,'2'}, + {1,"", M_LoadSelect,'3'}, + {1,"", M_LoadSelect,'4'}, + {1,"", M_LoadSelect,'5'}, + {1,"", M_LoadSelect,'6'}, + {1,"", M_LoadSelect,'7'}, //jff 3/15/98 extend number of slots + {1,"", M_LoadSelect,'8'}, +}; + +menu_t LoadDef = +{ + load_end, + &MainDef, + LoadMenue, + M_DrawLoad, + 80,34, //jff 3/15/98 move menu up + 0 +}; + +#define LOADGRAPHIC_Y 8 + +// [FG] delete a savegame + +static dboolean delete_verify = false; + +static void M_DrawDelVerify(void); + +static void M_DeleteGame(int i) +{ + char *name; + int len; + + len = G_SaveGameName(NULL, 0, i, false); + name = malloc(len+1); + G_SaveGameName(name, len+1, i, false); + M_remove(name); + free(name); + + M_ReadSaveStrings(); +} + +// +// M_LoadGame & Cie. +// + +void M_DrawLoad(void) +{ + int i; + + //jff 3/15/98 use symbolic load position + // CPhipps - patch drawing updated + V_DrawNamePatch(72 ,LOADGRAPHIC_Y, 0, "M_LOADG", CR_DEFAULT, VPT_STRETCH); + for (i = 0 ; i < load_end ; i++) { + M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i); + M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i], CR_DEFAULT); + } + + if (delete_verify) + M_DrawDelVerify(); +} + +// +// Draw border for the savegame description +// + +void M_DrawSaveLoadBorder(int x,int y) +{ + int i; + + V_DrawNamePatch(x-8, y+7, 0, "M_LSLEFT", CR_DEFAULT, VPT_STRETCH); + + for (i = 0 ; i < 24 ; i++) + { + V_DrawNamePatch(x, y+7, 0, "M_LSCNTR", CR_DEFAULT, VPT_STRETCH); + x += 8; + } + + V_DrawNamePatch(x, y+7, 0, "M_LSRGHT", CR_DEFAULT, VPT_STRETCH); +} + +// +// User wants to load this game +// + +void M_LoadSelect(int choice) +{ + // CPhipps - Modified so savegame filename is worked out only internal + // to g_game.c, this only passes the slot. + + G_LoadGame(choice, false); // killough 3/16/98, 5/15/98: add slot, cmd + + M_ClearMenus (); +} + +// +// killough 5/15/98: add forced loadgames +// + +static char *forced_loadgame_message; + +static void M_VerifyForcedLoadGame(int ch) +{ + if (ch=='y') + G_ForcedLoadGame(); + free(forced_loadgame_message); // free the message strdup()'ed below + M_ClearMenus(); +} + +void M_ForcedLoadGame(const char *msg) +{ + forced_loadgame_message = strdup(msg); // free()'d above + M_StartMessage(forced_loadgame_message, M_VerifyForcedLoadGame, true); +} + +// +// Selected from DOOM menu +// + +void M_LoadGame (int choice) +{ + delete_verify = false; + + /* killough 5/26/98: exclude during demo recordings + * cph - unless a new demo */ + if (demorecording && (compatibility_level < prboom_2_compatibility)) + { + M_StartMessage("you can't load a game\n" + "while recording an old demo!\n\n"PRESSKEY, + NULL, false); // killough 5/26/98: not externalized + return; + } + + M_SetupNextMenu(&LoadDef); + M_ReadSaveStrings(); +} + +///////////////////////////// +// +// SAVE GAME MENU +// + +// The definitions of the Save Game screen + +menuitem_t SaveMenu[]= +{ + {1,"", M_SaveSelect,'1'}, + {1,"", M_SaveSelect,'2'}, + {1,"", M_SaveSelect,'3'}, + {1,"", M_SaveSelect,'4'}, + {1,"", M_SaveSelect,'5'}, + {1,"", M_SaveSelect,'6'}, + {1,"", M_SaveSelect,'7'}, //jff 3/15/98 extend number of slots + {1,"", M_SaveSelect,'8'}, +}; + +menu_t SaveDef = +{ + load_end, // same number of slots as the Load Game screen + &MainDef, + SaveMenu, + M_DrawSave, + 80,34, //jff 3/15/98 move menu up + 0 +}; + +// +// M_ReadSaveStrings +// read the strings from the savegame files +// +void M_ReadSaveStrings(void) +{ + int i; + + for (i = 0 ; i < load_end ; i++) { + char *name; // killough 3/22/98 + int len; + FILE *fp; // killough 11/98: change to use stdio + + /* killough 3/22/98 + * cph - add not-demoplayback parameter */ + len = G_SaveGameName(NULL, 0, i, false); + name = malloc(len+1); + G_SaveGameName(name, len+1, i, false); + fp = M_fopen(name,"rb"); + free(name); + if (!fp) { // Ty 03/27/98 - externalized: + strcpy(&savegamestrings[i][0],s_EMPTYSTRING); + LoadMenue[i].status = 0; + continue; + } + fread(&savegamestrings[i], SAVESTRINGSIZE, 1, fp); + fclose(fp); + LoadMenue[i].status = 1; + } +} + +// +// M_SaveGame & Cie. +// +void M_DrawSave(void) +{ + int i; + + //jff 3/15/98 use symbolic load position + // CPhipps - patch drawing updated + V_DrawNamePatch(72, LOADGRAPHIC_Y, 0, "M_SAVEG", CR_DEFAULT, VPT_STRETCH); + for (i = 0 ; i < load_end ; i++) + { + M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i); + M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i], CR_DEFAULT); + } + + if (saveStringEnter) + { + i = M_StringWidth(savegamestrings[saveSlot]); + M_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*saveSlot,"_", CR_DEFAULT); + } + + if (delete_verify) + M_DrawDelVerify(); +} + +// +// M_Responder calls this when user is finished +// +static void M_DoSave(int slot) +{ + G_SaveGame (slot,savegamestrings[slot]); + M_ClearMenus (); + + // PICK QUICKSAVE SLOT YET? + if (quickSaveSlot == -2) + quickSaveSlot = slot; +} + +// [FG] generate a default save slot name if the user saves to an empty slot +static void SetDefaultSaveName (int slot) +{ + // map from IWAD or PWAD? + if (lumpinfo[maplumpnum].source == source_iwad) + { + snprintf(savegamestrings[itemOn], SAVESTRINGSIZE, + "%s", lumpinfo[maplumpnum].name); + } + else + { + char *wadname = (strdup)(lumpinfo[maplumpnum].wadfile->name); + char *ext = strrchr(wadname, '.'); + char *basename = wadname + strlen(wadname) - 1; + + if (ext != NULL) + *ext = '\0'; + + while (basename > wadname && *basename != '/' && *basename != '\\') + basename--; + if (*basename == '/' || *basename == '\\') + basename++; + + snprintf(savegamestrings[itemOn], SAVESTRINGSIZE, + "%s (%s)", lumpinfo[maplumpnum].name, + basename); + (free)(wadname); + } + + M_Strupr(savegamestrings[itemOn]); +} + +// [FG] override savegame name if it already starts with a map identifier +static dboolean StartsWithMapIdentifier (char *str) +{ + M_Strupr(str); + + if (strlen(str) >= 4 && + str[0] == 'E' && isdigit(str[1]) && + str[2] == 'M' && isdigit(str[3])) + { + return true; + } + + if (strlen(str) >= 5 && + str[0] == 'M' && str[1] == 'A' && str[2] == 'P' && + isdigit(str[3]) && isdigit(str[4])) + { + return true; + } + + return false; +} + +// +// User wants to save. Start string input for M_Responder +// +void M_SaveSelect(int choice) +{ + // we are going to be intercepting all chars + saveStringEnter = 1; + + saveSlot = choice; + strcpy(saveOldString,savegamestrings[choice]); + if (!strcmp(savegamestrings[choice],s_EMPTYSTRING) || // Ty 03/27/98 - externalized + StartsWithMapIdentifier(savegamestrings[choice])) + { + savegamestrings[choice][0] = 0; + SetDefaultSaveName(choice); + } + saveCharIndex = strlen(savegamestrings[choice]); +} + +// +// Selected from DOOM menu +// +void M_SaveGame (int choice) +{ + delete_verify = false; + + // killough 10/6/98: allow savegames during single-player demo playback + if (!usergame && (!demoplayback || netgame)) + { + M_StartMessage(s_SAVEDEAD,NULL,false); // Ty 03/27/98 - externalized + return; + } + + if (gamestate != GS_LEVEL) + return; + + M_SetupNextMenu(&SaveDef); + M_ReadSaveStrings(); +} + +///////////////////////////// +// +// OPTIONS MENU +// + +// numerical values for the Options menu items + +enum +{ + general, // killough 10/98 + // killough 4/6/98: move setup to be a sub-menu of OPTIONs + setup, // phares 3/21/98 + endgame, + messages, + /* detail, obsolete -- killough */ + scrnsize, + option_empty1, + mousesens, + /* option_empty2, submenu now -- killough */ + soundvol, + opt_end +} options_e; + +// The definitions of the Options menu + +menuitem_t OptionsMenu[]= +{ + // killough 4/6/98: move setup to be a sub-menu of OPTIONs + {1,"M_GENERL", M_General, 'g', "GENERAL"}, // killough 10/98 + {1,"M_SETUP", M_Setup, 's', "SETUP"}, // phares 3/21/98 + {1,"M_ENDGAM", M_EndGame,'e', "END GAME"}, + {1,"M_MESSG", M_ChangeMessages,'m', "MESSAGES:"}, + /* {1,"M_DETAIL", M_ChangeDetail,'g'}, unused -- killough */ + {2,"M_SCRNSZ", M_SizeDisplay,'s', "SCREEN SIZE"}, + {-1,"",0}, + {1,"M_MSENS", M_ChangeSensitivity,'m', "MOUSE SENSITIVITY"}, + /* {-1,"",0}, replaced with submenu -- killough */ + {1,"M_SVOL", M_Sound,'s', "SOUND VOLUME"}, +}; + +menu_t OptionsDef = +{ + opt_end, + &MainDef, + OptionsMenu, + M_DrawOptions, + 60,37, + 0 +}; + +// +// M_Options +// +char detailNames[2][9] = {"M_GDHIGH","M_GDLOW"}; +char msgNames[2][9] = {"M_MSGOFF","M_MSGON"}; + + +void M_DrawOptions(void) +{ + // CPhipps - patch drawing updated + // proff/nicolas 09/20/98 -- changed for hi-res + V_DrawNamePatch(108, 15, 0, "M_OPTTTL", CR_DEFAULT, VPT_STRETCH); + + if ((W_CheckNumForName("M_GENERL") < 0) || (W_CheckNumForName("M_SETUP") < 0)) + { + M_WriteText(OptionsDef.x + M_StringWidth("MESSAGES: "), + OptionsDef.y+8-(M_StringHeight("ONOFF")/2)+LINEHEIGHT*messages, + showMessages ? "ON" : "OFF", CR_DEFAULT); + } + else + { + V_DrawNamePatch(OptionsDef.x + 120, OptionsDef.y+LINEHEIGHT*messages, 0, + msgNames[showMessages], CR_DEFAULT, VPT_STRETCH); + } + + M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(scrnsize+1), + 9,screenSize); +} + +void M_Options(int choice) +{ + M_SetupNextMenu(&OptionsDef); +} + +///////////////////////////// +// +// M_QuitDOOM +// +int quitsounds[8] = +{ + sfx_pldeth, + sfx_dmpain, + sfx_popain, + sfx_slop, + sfx_telept, + sfx_posit1, + sfx_posit3, + sfx_sgtatk +}; + +int quitsounds2[8] = +{ + sfx_vilact, + sfx_getpow, + sfx_boscub, + sfx_slop, + sfx_skeswg, + sfx_kntdth, + sfx_bspact, + sfx_sgtatk +}; + +static void M_QuitResponse(int ch) +{ + if (ch != 'y') + return; + + //e6y: Optional removal of a quit sound + if ((!netgame && showendoom) // killough 12/98 + && !nosfxparm && snd_card) // avoid delay if no sound card + { + int i; + + if (gamemode == commercial) + S_StartSound(NULL,quitsounds2[(gametic>>2)&7]); + else + S_StartSound(NULL,quitsounds[(gametic>>2)&7]); + + // wait till all sounds stopped or 3 seconds are over + i = 30; + while (i>0) { + I_uSleep(100000); // CPhipps - don't thrash cpu in this loop + if (!I_AnySoundStillPlaying()) + break; + i--; + } + } + //e6y: I_SafeExit instead of exit - prevent recursive exits + I_SafeExit(0); // killough +} + +void M_QuitDOOM(int choice) +{ + static char endstring[160]; + + // We pick index 0 which is language sensitive, + // or one at random, between 1 and maximum number. + // Ty 03/27/98 - externalized DOSY as a string s_DOSY that's in the sprintf + if (language != english) + sprintf(endstring,"%s\n\n%s",s_DOSY, endmsg[0] ); + else // killough 1/18/98: fix endgame message calculation: + sprintf(endstring,"%s\n\n%s", endmsg[gametic%(NUM_QUITMESSAGES-1)+1], s_DOSY); + + M_StartMessage(endstring,M_QuitResponse,true); +} + +///////////////////////////// +// +// SOUND VOLUME MENU +// + +// numerical values for the Sound Volume menu items +// The 'empty' slots are where the sliding scales appear. + +enum +{ + sfx_vol, + sfx_empty1, + music_vol, + sfx_empty2, + sound_end +} sound_e; + +// The definitions of the Sound Volume menu + +menuitem_t SoundMenu[]= +{ + {2,"M_SFXVOL",M_SfxVol,'s'}, + {-1,"",0}, + {2,"M_MUSVOL",M_MusicVol,'m'}, + {-1,"",0} +}; + +menu_t SoundDef = +{ + sound_end, + &OptionsDef, + SoundMenu, + M_DrawSound, + 80,64, + 0 +}; + +// +// Change Sfx & Music volumes +// + +void M_DrawSound(void) +{ + // CPhipps - patch drawing updated + V_DrawNamePatch(60, 38, 0, "M_SVOL", CR_DEFAULT, VPT_STRETCH); + + M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_vol+1),16,snd_SfxVolume); + + M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(music_vol+1),16,snd_MusicVolume); +} + +void M_Sound(int choice) +{ + M_SetupNextMenu(&SoundDef); +} + +void M_SfxVol(int choice) +{ + switch(choice) + { + case 0: + if (snd_SfxVolume) + snd_SfxVolume--; + break; + case 1: + if (snd_SfxVolume < 15) + snd_SfxVolume++; + break; + } + + S_SetSfxVolume(snd_SfxVolume /* *8 */); +} + +void M_MusicVol(int choice) +{ + switch(choice) + { + case 0: + if (snd_MusicVolume) + snd_MusicVolume--; + break; + case 1: + if (snd_MusicVolume < 15) + snd_MusicVolume++; + break; + } + + S_SetMusicVolume(snd_MusicVolume /* *8 */); +} + +///////////////////////////// +// +// MOUSE SENSITIVITY MENU -- killough +// + +// numerical values for the Mouse Sensitivity menu items +// The 'empty' slots are where the sliding scales appear. + +enum +{ + mouse_horiz, + mouse_empty1, + mouse_vert, + mouse_empty2, + +//e6y + mouse_mlook, + mouse_empty3, + mouse_accel, + mouse_empty4, + + mouse_end +} mouse_e; + +// The definitions of the Mouse Sensitivity menu + +menuitem_t MouseMenu[]= +{ + {2,"M_HORSEN",M_MouseHoriz,'h', "HORIZONTAL"}, + {-1,"",0}, + {2,"M_VERSEN",M_MouseVert,'v', "VERTICAL"}, + {-1,"",0} + + //e6y + , + {2,"M_LOKSEN",M_MouseMLook,'l', "MOUSE LOOK"}, + {-1,"",0}, + {2,"M_ACCEL",M_MouseAccel,'a', "ACCELERATION"}, + {-1,"",0} +}; + +menu_t MouseDef = +{ + mouse_end, + &OptionsDef, + MouseMenu, + M_DrawMouse, + 60,37,//e6y + 0 +}; + + +// I'm using a scale of 100 since I don't know what's normal -- killough. + +#define MOUSE_SENS_MAX 100 + +// +// Change Mouse Sensitivities -- killough +// + +void M_DrawMouse(void) +{ + int mhmx,mvmx; /* jff 4/3/98 clamp drawn position 99max mead */ + + // CPhipps - patch drawing updated + V_DrawNamePatch(60, 15, 0, "M_MSENS", CR_DEFAULT, VPT_STRETCH);//e6y + + //jff 4/3/98 clamp horizontal sensitivity display + mhmx = mouseSensitivity_horiz>99? 99 : mouseSensitivity_horiz; /*mead*/ + M_DrawThermo(MouseDef.x,MouseDef.y+LINEHEIGHT*(mouse_horiz+1),100,mhmx); + //jff 4/3/98 clamp vertical sensitivity display + mvmx = mouseSensitivity_vert>99? 99 : mouseSensitivity_vert; /*mead*/ + M_DrawThermo(MouseDef.x,MouseDef.y+LINEHEIGHT*(mouse_vert+1),100,mvmx); + + //e6y + { + int mpmx; + mpmx = mouseSensitivity_mlook>99? 99 : mouseSensitivity_mlook; + M_DrawThermo(MouseDef.x,MouseDef.y+LINEHEIGHT*(mouse_mlook+1),100,mpmx); + mpmx = mouse_acceleration>99? 99 : mouse_acceleration; + M_DrawThermo(MouseDef.x,MouseDef.y+LINEHEIGHT*(mouse_accel+1),100,mpmx); + } +} + +void M_ChangeSensitivity(int choice) +{ + M_SetupNextMenu(&MouseDef); // killough + + // switch(choice) + // { + // case 0: + // if (mouseSensitivity) + // mouseSensitivity--; + // break; + // case 1: + // if (mouseSensitivity < 9) + // mouseSensitivity++; + // break; + // } +} + +void M_MouseHoriz(int choice) +{ + M_Mouse(choice, &mouseSensitivity_horiz); +} + +void M_MouseVert(int choice) +{ + M_Mouse(choice, &mouseSensitivity_vert); +} + +void M_Mouse(int choice, int *sens) +{ + switch(choice) + { + case 0: + if (*sens) + --*sens; + break; + case 1: + if (*sens < 99) + ++*sens; /*mead*/ + break; + } +} + +///////////////////////////// +// +// M_QuickSave +// + +char tempstring[80]; + +static void M_QuickSaveResponse(int ch) +{ + if (ch == 'y') { + M_DoSave(quickSaveSlot); + S_StartSound(NULL,sfx_swtchx); + } +} + +void M_QuickSave(void) +{ + if (!usergame && (!demoplayback || netgame)) { /* killough 10/98 */ + S_StartSound(NULL,sfx_oof); + return; + } + + if (gamestate != GS_LEVEL) + return; + + if (quickSaveSlot < 0) { + M_StartControlPanel(); + M_ReadSaveStrings(); + M_SetupNextMenu(&SaveDef); + quickSaveSlot = -2; // means to pick a slot now + return; + } + sprintf(tempstring,s_QSPROMPT,savegamestrings[quickSaveSlot]); // Ty 03/27/98 - externalized + M_StartMessage(tempstring,M_QuickSaveResponse,true); +} + +///////////////////////////// +// +// M_QuickLoad +// + +static void M_QuickLoadResponse(int ch) +{ + if (ch == 'y') { + M_LoadSelect(quickSaveSlot); + S_StartSound(NULL,sfx_swtchx); + } +} + +void M_QuickLoad(void) +{ + // cph - removed restriction against quickload in a netgame + + if (demorecording) { // killough 5/26/98: exclude during demo recordings + M_StartMessage("you can't quickload\n" + "while recording a demo!\n\n"PRESSKEY, + NULL, false); // killough 5/26/98: not externalized + return; + } + + if (quickSaveSlot < 0) { + M_StartMessage(s_QSAVESPOT,NULL,false); // Ty 03/27/98 - externalized + return; + } + sprintf(tempstring,s_QLPROMPT,savegamestrings[quickSaveSlot]); // Ty 03/27/98 - externalized + M_StartMessage(tempstring,M_QuickLoadResponse,true); +} + +///////////////////////////// +// +// M_EndGame +// + +static void M_EndGameResponse(int ch) +{ + if (ch != 'y') + return; + + // killough 5/26/98: make endgame quit if recording or playing back demo + if (demorecording || singledemo) + G_CheckDemoStatus(); + + currentMenu->lastOn = itemOn; + M_ClearMenus (); + D_StartTitle (); +} + +void M_EndGame(int choice) +{ + if (netgame) + { + M_StartMessage(s_NETEND,NULL,false); // Ty 03/27/98 - externalized + return; + } + M_StartMessage(s_ENDGAME,M_EndGameResponse,true); // Ty 03/27/98 - externalized +} + +///////////////////////////// +// +// Toggle messages on/off +// + +void M_ChangeMessages(int choice) +{ + // warning: unused parameter `int choice' + choice = 0; + showMessages = 1 - showMessages; + + if (!showMessages) + players[consoleplayer].message = s_MSGOFF; // Ty 03/27/98 - externalized + else + players[consoleplayer].message = s_MSGON ; // Ty 03/27/98 - externalized + + message_dontfuckwithme = true; +} + +///////////////////////////// +// +// CHANGE DISPLAY SIZE +// +// jff 2/23/98 restored to pre-HUD state +// hud_active controlled soley by F5=key_detail (key_hud) +// hud_displayed is toggled by + or = in fullscreen +// hud_displayed is cleared by - + +void M_SizeDisplay(int choice) +{ + switch(choice) { + case 0: + if (screenSize > 0) { + screenblocks--; + screenSize--; + hud_displayed = 0; + } + break; + case 1: + if (screenSize < 8) { + screenblocks++; + screenSize++; + } + else + hud_displayed = !hud_displayed; + break; + } + R_SetViewSize (screenblocks /*, detailLevel obsolete -- killough */); +} + +// +// End of Original Menus +// +///////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// +// SETUP MENU (phares) +// +// We've added a set of Setup Screens from which you can configure a number +// of variables w/o having to restart the game. There are 7 screens: +// +// Key Bindings +// Weapons +// Status Bar / HUD +// Automap +// Enemies +// Messages +// Chat Strings +// +// killough 10/98: added Compatibility and General menus +// + +///////////////////////////// +// +// booleans for setup screens +// these tell you what state the setup screens are in, and whether any of +// the overlay screens (automap colors, reset button message) should be +// displayed + +dboolean setup_active = false; // in one of the setup screens +dboolean set_keybnd_active = false; // in key binding setup screens +dboolean set_weapon_active = false; // in weapons setup screen +dboolean set_status_active = false; // in status bar/hud setup screen +dboolean set_auto_active = false; // in automap setup screen +dboolean set_enemy_active = false; // in enemies setup screen +dboolean set_mess_active = false; // in messages setup screen +dboolean set_chat_active = false; // in chat string setup screen +dboolean setup_select = false; // changing an item +dboolean setup_gather = false; // gathering keys for value +dboolean colorbox_active = false; // color palette being shown +dboolean default_verify = false; // verify reset defaults decision +dboolean set_general_active = false; +dboolean set_compat_active = false; + +///////////////////////////// +// +// set_menu_itemon is an index that starts at zero, and tells you which +// item on the current screen the cursor is sitting on. +// +// current_setup_menu is a pointer to the current setup menu table. + +static int set_menu_itemon; // which setup item is selected? // phares 3/98 +static setup_menu_t* current_setup_menu; // points to current setup menu table + +// save the setup menu's itemon value in the S_END element's x coordinate + +static int M_GetSetupMenuItemOn (void) +{ + const setup_menu_t* menu = current_setup_menu; + + if (menu) + { + while (!(menu->m_flags & S_END)) + menu++; + + return menu->m_x; + } + + return 0; +} + +static void M_SetSetupMenuItemOn (const int x) +{ + setup_menu_t* menu = current_setup_menu; + + if (menu) + { + while (!(menu->m_flags & S_END)) + menu++; + + menu->m_x = x; + } +} + +///////////////////////////// +// +// The menu_buffer is used to construct strings for display on the screen. + +static char menu_buffer[66]; + +///////////////////////////// +// +// The setup_e enum is used to provide a unique number for each group of Setup +// Screens. + +enum +{ + set_compat, + set_key_bindings, + set_weapons, + set_statbar, + set_automap, + set_enemy, + set_messages, + set_chatstrings, + set_setup_end +} setup_e; + +int setup_screen; // the current setup screen. takes values from setup_e + +///////////////////////////// +// +// SetupMenu is the definition of what the main Setup Screen should look +// like. Each entry shows that the cursor can land on each item (1), the +// built-in graphic lump (i.e. "M_KEYBND") that should be displayed, +// the program which takes over when an item is selected, and the hotkey +// associated with the item. + +menuitem_t SetupMenu[]= +{ + {1,"M_COMPAT",M_Compat, 'p', "DOOM COMPATIBILITY"}, + {1,"M_KEYBND",M_KeyBindings,'k', "KEY BINDINGS"}, + {1,"M_WEAP" ,M_Weapons, 'w', "WEAPONS"}, + {1,"M_STAT" ,M_StatusBar, 's', "STATUS BAR / HUD"}, + {1,"M_AUTO" ,M_Automap, 'a', "AUTOMAP"}, + {1,"M_ENEM" ,M_Enemy, 'e', "ENEMIES"}, + {1,"M_MESS" ,M_Messages, 'm', "MESSAGES"}, + {1,"M_CHAT" ,M_ChatStrings,'c', "CHAT STRINGS"}, +}; + +///////////////////////////// +// +// M_DoNothing does just that: nothing. Just a placeholder. + +static void M_DoNothing(int choice) +{ +} + +///////////////////////////// +// +// Items needed to satisfy the 'Big Font' menu structures: +// +// the generic_setup_e enum mimics the 'Big Font' menu structures, but +// means nothing to the Setup Menus. + +enum +{ + generic_setupempty1, + generic_setup_end +} generic_setup_e; + +// Generic_Setup is a do-nothing definition that the mainstream Menu code +// can understand, while the Setup Menu code is working. Another placeholder. + +menuitem_t Generic_Setup[] = +{ + {1,"",M_DoNothing,0} +}; + +///////////////////////////// +// +// SetupDef is the menu definition that the mainstream Menu code understands. +// This is used by M_Setup (below) to define what is drawn and what is done +// with the main Setup screen. + +menu_t SetupDef = +{ + set_setup_end, // number of Setup Menu items (Key Bindings, etc.) + &OptionsDef, // menu to return to when BACKSPACE is hit on this menu + SetupMenu, // definition of items to show on the Setup Screen + M_DrawSetup, // program that draws the Setup Screen + 59,37, // x,y position of the skull (modified when the skull is + // drawn). The skull is parked on the upper-left corner + // of the Setup screens, since it isn't needed as a cursor + 0 // last item the user was on for this menu +}; + +///////////////////////////// +// +// Here are the definitions of the individual Setup Menu screens. They +// follow the format of the 'Big Font' menu structures. See the comments +// for SetupDef (above) to help understand what each of these says. + +menu_t KeybndDef = +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawKeybnd, + 34,5, // skull drawn here + 0 +}; + +menu_t WeaponDef = +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawWeapons, + 34,5, // skull drawn here + 0 +}; + +menu_t StatusHUDDef = +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawStatusHUD, + 34,5, // skull drawn here + 0 +}; + +menu_t AutoMapDef = +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawAutoMap, + 34,5, // skull drawn here + 0 +}; + +menu_t EnemyDef = // phares 4/08/98 +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawEnemy, + 34,5, // skull drawn here + 0 +}; + +menu_t MessageDef = // phares 4/08/98 +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawMessages, + 34,5, // skull drawn here + 0 +}; + +menu_t ChatStrDef = // phares 4/10/98 +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawChatStrings, + 34,5, // skull drawn here + 0 +}; + +menu_t GeneralDef = // killough 10/98 +{ + generic_setup_end, + &OptionsDef, + Generic_Setup, + M_DrawGeneral, + 34,5, // skull drawn here + 0 +}; + +menu_t CompatDef = // killough 10/98 +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawCompat, + 34,5, // skull drawn here + 0 +}; + +///////////////////////////// +// +// Draws the Title for the main Setup screen + +void M_DrawSetup(void) +{ + // CPhipps - patch drawing updated + M_DrawTitle(124, 15, "M_SETUP", CR_DEFAULT, "SETUP", CR_GOLD); +} + +///////////////////////////// +// +// Uses the SetupDef structure to draw the menu items for the main +// Setup screen + +void M_Setup(int choice) +{ + M_SetupNextMenu(&SetupDef); +} + +///////////////////////////// +// +// Data that's used by the Setup screen code +// +// Establish the message colors to be used + +#define CR_TITLE CR_GOLD +#define CR_SET CR_GREEN +#define CR_ITEM CR_RED +#define CR_HILITE CR_ORANGE +#define CR_SELECT CR_GRAY + +//e6y +#define CR_DISABLE CR_GRAY + +// Data used by the Automap color selection code + +#define CHIP_SIZE 7 // size of color block for colored items + +#define COLORPALXORIG ((320 - 16*(CHIP_SIZE+1))/2) +#define COLORPALYORIG ((200 - 16*(CHIP_SIZE+1))/2) + +#define PAL_BLACK 0 +#define PAL_WHITE 4 + +// Data used by the Chat String editing code + +#define CHAT_STRING_BFR_SIZE 128 + +// chat strings must fit in this screen space +// killough 10/98: reduced, for more general uses +#define MAXCHATWIDTH 272 + +int chat_index; +char* chat_string_buffer; // points to new chat strings while editing + +///////////////////////////// +// +// phares 4/17/98: +// Added 'Reset to Defaults' Button to Setup Menu screens +// This is a small button that sits in the upper-right-hand corner of +// the first screen for each group. It blinks when selected, thus the +// two patches, which it toggles back and forth. + +char ResetButtonName[2][8] = {"M_BUTT1","M_BUTT2"}; + +///////////////////////////// +// +// phares 4/18/98: +// Consolidate Item drawing code +// +// M_DrawItem draws the description of the provided item (the left-hand +// part). A different color is used for the text depending on whether the +// item is selected or not, or whether it's about to change. + +// CPhipps - static, hanging else removed, const parameter +static void M_DrawItem(const setup_menu_t* s) +{ + int x = s->m_x; + int y = s->m_y; + int flags = s->m_flags; + if (flags & S_RESET) + + // This item is the reset button + // Draw the 'off' version if this isn't the current menu item + // Draw the blinking version in tune with the blinking skull otherwise + + // proff/nicolas 09/20/98 -- changed for hi-res + // CPhipps - Patch drawing updated, reformatted + + V_DrawNamePatch(x, y, 0, ResetButtonName[(flags & (S_HILITE|S_SELECT)) ? whichSkull : 0], + CR_DEFAULT, VPT_STRETCH); + + else { // Draw the item string + char *p, *t; + int w = 0; + int color = + flags & S_DISABLE ? CR_DISABLE : //e6y + flags & S_SELECT ? CR_SELECT : + flags & S_HILITE ? CR_HILITE : + flags & (S_TITLE|S_NEXT|S_PREV) ? CR_TITLE : CR_ITEM; // killough 10/98 + + /* killough 10/98: + * Enhance to support multiline text separated by newlines. + * This supports multiline items on horizontally-crowded menus. + */ + + for (p = t = strdup(s->m_text); (p = strtok(p,"\n")); y += 8, p = NULL) + { /* killough 10/98: support left-justification: */ + strcpy(menu_buffer,p); + if (!(flags & S_LEFTJUST)) + w = M_GetPixelWidth(menu_buffer) + 4; + M_DrawMenuString(x - w, y ,color); + // print a blinking "arrow" next to the currently highlighted menu item + if (s == current_setup_menu + set_menu_itemon && whichSkull) + M_DrawString(x - w - 8, y, color, ">"); + } + free(t); + } +} + +// If a number item is being changed, allow up to N keystrokes to 'gather' +// the value. Gather_count tells you how many you have so far. The legality +// of what is gathered is determined by the low/high settings for the item. + +#define MAXGATHER 5 +int gather_count; +char gather_buffer[MAXGATHER+1]; // killough 10/98: make input character-based + +///////////////////////////// +// +// phares 4/18/98: +// Consolidate Item Setting drawing code +// +// M_DrawSetting draws the setting of the provided item (the right-hand +// part. It determines the text color based on whether the item is +// selected or being changed. Then, depending on the type of item, it +// displays the appropriate setting value: yes/no, a key binding, a number, +// a paint chip, etc. + +static void M_DrawSetting(const setup_menu_t* s) +{ + int x = s->m_x, y = s->m_y, flags = s->m_flags, color; + + // Determine color of the text. This may or may not be used later, + // depending on whether the item is a text string or not. + + color = + flags & S_DISABLE ? CR_DISABLE : //e6y + flags & S_SELECT ? CR_SELECT : flags & S_HILITE ? CR_HILITE : CR_SET; + + // Is the item a YES/NO item? + + if (flags & S_YESNO) { + strcpy(menu_buffer,*s->var.def->location.pi ? "YES" : "NO"); + if (s == current_setup_menu + set_menu_itemon && whichSkull && !setup_select) + strcat(menu_buffer, " <"); + M_DrawMenuString(x,y,color); + return; + } + + // Is the item a simple number? + + if (flags & S_NUM) { + // killough 10/98: We must draw differently for items being gathered. + if (flags & (S_HILITE|S_SELECT) && setup_gather) { + gather_buffer[gather_count] = 0; + strcpy(menu_buffer, gather_buffer); + } + else + sprintf(menu_buffer,"%d",*s->var.def->location.pi); + if (s == current_setup_menu + set_menu_itemon && whichSkull && !setup_select) + strcat(menu_buffer, " <"); + M_DrawMenuString(x,y,color); + return; + } + + // Is the item a key binding? + + if (flags & S_KEY) { // Key Binding + int *key = s->var.m_key; + + // Draw the key bound to the action + + if (key) { + M_GetKeyString(*key,0); // string to display + + if (s->m_mouse && *s->m_mouse != -1) + sprintf(menu_buffer+strlen(menu_buffer), "/MB%d", + *s->m_mouse+1); + if (s->m_joy) + sprintf(menu_buffer+strlen(menu_buffer), "/JSB%d", + *s->m_joy+1); + + if (s == current_setup_menu + set_menu_itemon && whichSkull && !setup_select) + strcat(menu_buffer, " <"); + M_DrawMenuString(x,y,color); + } + return; + } + + // Is the item a weapon number? + // OR, Is the item a colored text string from the Automap? + // + // killough 10/98: removed special code, since the rest of the engine + // already takes care of it, and this code prevented the user from setting + // their overall weapons preferences while playing Doom 1. + // + // killough 11/98: consolidated weapons code with color range code + + if (flags & (S_WEAP|S_CRITEM)) // weapon number or color range + { + sprintf(menu_buffer,"%d", *s->var.def->location.pi); + if (s == current_setup_menu + set_menu_itemon && whichSkull && !setup_select) + M_DrawString(x + 8, y, color, " <"); + M_DrawMenuString(x,y, flags & S_CRITEM ? *s->var.def->location.pi : color); + return; + } + + // Is the item a paint chip? + + if (flags & S_COLOR) // Automap paint chip + { + int ch; + + ch = *s->var.def->location.pi; + // proff 12/6/98: Drawing of colorchips completly changed for hi-res, it now uses a patch + // draw the paint chip + // e6y: wide-res + { + int xx = x, yy = y - 1, ww = 8, hh = 8; + V_GetWideRect(&xx, &yy, &ww, &hh, VPT_STRETCH); + V_FillRect(0, xx, yy, ww, hh, PAL_BLACK); + xx = x + 1, yy = y, ww = 6, hh = 6; + V_GetWideRect(&xx, &yy, &ww, &hh, VPT_STRETCH); + V_FillRect(0, xx, yy, ww, hh, (byte)ch); + } + + if (!ch) // don't show this item in automap mode + V_DrawNamePatch(x+1,y,0,"M_PALNO", CR_DEFAULT, VPT_STRETCH); + if (s == current_setup_menu + set_menu_itemon && whichSkull && !setup_select) + M_DrawString(x + 8, y, color, " <"); + return; + } + + // Is the item a chat string? + // killough 10/98: or a filename? + + if (flags & S_STRING) { + /* cph - cast to char* as it's really a Z_Strdup'd string (see m_misc.h) */ + union { const char **c; char **s; } u; // type punning via unions + char *text; + + u.c = s->var.def->location.ppsz; + text = *(u.s); + + // Are we editing this string? If so, display a cursor under + // the correct character. + + if (setup_select && (s->m_flags & (S_HILITE|S_SELECT))) { + int cursor_start, char_width; + char c[2]; + + // If the string is too wide for the screen, trim it back, + // one char at a time until it fits. This should only occur + // while you're editing the string. + + while (M_GetPixelWidth(text) >= MAXCHATWIDTH) { + int len = strlen(text); + text[--len] = 0; + if (chat_index > len) + chat_index--; + } + + // Find the distance from the beginning of the string to + // where the cursor should be drawn, plus the width of + // the char the cursor is under.. + + *c = text[chat_index]; // hold temporarily + c[1] = 0; + char_width = M_GetPixelWidth(c); + if (char_width == 1) + char_width = 7; // default for end of line + text[chat_index] = 0; // NULL to get cursor position + cursor_start = M_GetPixelWidth(text); + text[chat_index] = *c; // replace stored char + + // Now draw the cursor + // proff 12/6/98: Drawing of cursor changed for hi-res + // e6y: wide-res + if (x + cursor_start + char_width < BASE_WIDTH) + { + int xx = (x+cursor_start-1), yy = y, ww = char_width, hh = 9; + V_GetWideRect(&xx, &yy, &ww, &hh, VPT_STRETCH); + V_FillRect(0, xx, yy, ww, hh, PAL_WHITE); + } + } + + // Draw the setting for the item + + strcpy(menu_buffer,text); + if (s == current_setup_menu + set_menu_itemon && whichSkull && !setup_select) + strcat(menu_buffer, " <"); + M_DrawMenuString(x,y,color); + return; + } + + // Is the item a selection of choices? + + if (flags & S_CHOICE) { + if (s->var.def->type == def_int) { + if (s->selectstrings == NULL) { + sprintf(menu_buffer,"%d",*s->var.def->location.pi); + } else { + strcpy(menu_buffer,s->selectstrings[*s->var.def->location.pi]); + } + } + + if (s->var.def->type == def_str) { + sprintf(menu_buffer,"%s", *s->var.def->location.ppsz); + } + + if (s == current_setup_menu + set_menu_itemon && whichSkull && !setup_select) + strcat(menu_buffer, " <"); + M_DrawMenuString(x,y,color); + return; + } +} + +///////////////////////////// +// +// M_DrawScreenItems takes the data for each menu item and gives it to +// the drawing routines above. + +// CPhipps - static, const parameter, formatting +static void M_DrawScreenItems(const setup_menu_t* src) +{ + if (print_warning_about_changes > 0) { /* killough 8/15/98: print warning */ + //e6y + if (warning_about_changes & S_CANT_GL_ARB_MULTITEXTURE) { + strcpy(menu_buffer, "Extension GL_ARB_multitexture not found"); + M_DrawMenuString(30,176,CR_RED); + } else + if (warning_about_changes & S_CANT_GL_ARB_MULTISAMPLEFACTOR) { + strcpy(menu_buffer, "Mast be even number like 0-none, 2, 4, 6"); + M_DrawMenuString(30,176,CR_RED); + } else + + if (warning_about_changes & S_BADVAL) { + strcpy(menu_buffer, "Value out of Range"); + M_DrawMenuString(100,176,CR_RED); + } else if (warning_about_changes & S_PRGWARN) { + strcpy(menu_buffer, "Warning: Program must be restarted to see changes"); + M_DrawMenuString(3, 176, CR_RED); + } else if (warning_about_changes & S_BADVID) { + strcpy(menu_buffer, "Video mode not supported"); + M_DrawMenuString(80,176,CR_RED); + } else { + strcpy(menu_buffer, "Warning: Changes are pending until next game"); + M_DrawMenuString(18,184,CR_RED); + } + } + + while (!(src->m_flags & S_END)) { + + // See if we're to draw the item description (left-hand part) + + if (src->m_flags & S_SHOWDESC) + M_DrawItem(src); + + // See if we're to draw the setting (right-hand part) + + if (src->m_flags & S_SHOWSET) + M_DrawSetting(src); + src++; + } +} + +///////////////////////////// +// +// Data used to draw the "are you sure?" dialogue box when resetting +// to defaults. + +#define VERIFYBOXXORG 66 +#define VERIFYBOXYORG 88 + +// And the routine to draw it. + +static void M_DrawDefVerify(void) +{ + // proff 12/6/98: Drawing of verify box changed for hi-res, it now uses a patch + V_DrawNamePatch(VERIFYBOXXORG,VERIFYBOXYORG,0,"M_VBOX",CR_DEFAULT,VPT_STRETCH); + // The blinking messages is keyed off of the blinking of the + // cursor skull. + + if (whichSkull) { // blink the text + strcpy(menu_buffer,"Reset to defaults? (Y or N)"); + M_DrawMenuString(VERIFYBOXXORG+8,VERIFYBOXYORG+8,CR_RED); + } +} + +// [FG] delete a savegame + +static void M_DrawDelVerify(void) +{ + V_DrawNamePatch(VERIFYBOXXORG,VERIFYBOXYORG,0,"M_VBOX",CR_DEFAULT,VPT_STRETCH); + + if (whichSkull) { + strcpy(menu_buffer,"Delete savegame? (Y or N)"); + M_DrawMenuString(VERIFYBOXXORG+8,VERIFYBOXYORG+8,CR_RED); + } +} + +///////////////////////////// +// +// phares 4/18/98: +// M_DrawInstructions writes the instruction text just below the screen title +// +// cph 2006/08/06 - go back to the Boom version, and then clean up by using +// M_DrawStringCentered (much better than all those magic 'x' valies!) + +static void M_DrawInstructions(void) +{ + int flags = current_setup_menu[set_menu_itemon].m_flags; + + // There are different instruction messages depending on whether you + // are changing an item or just sitting on it. + + if (setup_select) { + switch (flags & (S_KEY | S_YESNO | S_WEAP | S_NUM | S_COLOR | S_CRITEM | S_CHAT | S_RESET | S_FILE | S_CHOICE)) { + case S_KEY: + // See if a joystick or mouse button setting is allowed for + // this item. + if (current_setup_menu[set_menu_itemon].m_mouse || current_setup_menu[set_menu_itemon].m_joy) + M_DrawStringCentered(160, 20, CR_SELECT, "Press key or button for this action"); + else + M_DrawStringCentered(160, 20, CR_SELECT, "Press key for this action"); + break; + + case S_YESNO: + M_DrawStringCentered(160, 20, CR_SELECT, "Press ENTER key to toggle"); + break; + case S_WEAP: + M_DrawStringCentered(160, 20, CR_SELECT, "Enter weapon number"); + break; + case S_NUM: + M_DrawStringCentered(160, 20, CR_SELECT, "Enter value. Press ENTER when finished."); + break; + case S_COLOR: + M_DrawStringCentered(160, 20, CR_SELECT, "Select color and press enter"); + break; + case S_CRITEM: + M_DrawStringCentered(160, 20, CR_SELECT, "Enter value"); + break; + case S_CHAT: + M_DrawStringCentered(160, 20, CR_SELECT, "Type/edit chat string and Press ENTER"); + break; + case S_FILE: + M_DrawStringCentered(160, 20, CR_SELECT, "Type/edit filename and Press ENTER"); + break; + case S_CHOICE: + M_DrawStringCentered(160, 20, CR_SELECT, "Press left or right to choose"); + break; + case S_RESET: + break; +#ifdef SIMPLECHECKS + default: + lprintf(LO_WARN,"Unrecognised menu item type %d", flags); +#endif + } + } else { + if (flags & S_RESET) + M_DrawStringCentered(160, 20, CR_HILITE, "Press ENTER key to reset to defaults"); + else if (flags & S_KEY) + M_DrawStringCentered(160, 20, CR_HILITE, "Press Enter to Change, Del to Clear"); + else + M_DrawStringCentered(160, 20, CR_HILITE, "Press Enter to Change"); + } +} + + +///////////////////////////// +// +// The Key Binding Screen tables. + +#define KB_X 160 +#define KB_PREV 57 +#define KB_NEXT 310 +#define KB_Y 31 + +// phares 4/16/98: +// X,Y position of reset button. This is the same for every screen, and is +// only defined once here. + +#define X_BUTTON 301 +#define Y_BUTTON 3 + +// Definitions of the (in this case) four key binding screens. + +setup_menu_t keys_settings1[]; +setup_menu_t keys_settings2[]; +setup_menu_t keys_settings3[]; +setup_menu_t keys_settings4[]; +setup_menu_t keys_settings5[]; +setup_menu_t keys_settings6[]; +setup_menu_t keys_settings7[]; + +// The table which gets you from one screen table to the next. + +setup_menu_t* keys_settings[] = +{ + keys_settings1, + keys_settings2, + keys_settings3, + keys_settings4, + keys_settings5, + keys_settings6, + keys_settings7, + NULL +}; + +int mult_screens_index; // the index of the current screen in a set + +// Here's an example from this first screen, with explanations. +// +// { +// "STRAFE", // The description of the item ('strafe' key) +// S_KEY, // This is a key binding item +// m_scrn, // It belongs to the m_scrn group. Its key cannot be +// // bound to two items in this group. +// KB_X, // The X offset of the start of the right-hand side +// KB_Y+ 8*8, // The Y offset of the start of the right-hand side. +// // Always given in multiples off a baseline. +// &key_strafe, // The variable that holds the key value bound to this +// OR a string that holds the config variable name. +// OR a pointer to another setup_menu +// &mousebstrafe, // The variable that holds the mouse button bound to + // this. If zero, no mouse button can be bound here. +// &joybstrafe, // The variable that holds the joystick button bound to + // this. If zero, no mouse button can be bound here. +// } + +// The first Key Binding screen table. +// Note that the Y values are ascending. If you need to add something to +// this table, (well, this one's not a good example, because it's full) +// you need to make sure the Y values still make sense so everything gets +// displayed. +// +// Note also that the first screen of each set has a line for the reset +// button. If there is more than one screen in a set, the others don't get +// the reset button. +// +// Note also that this screen has a "NEXT ->" line. This acts like an +// item, in that 'activating' it moves you along to the next screen. If +// there's a "<- PREV" item on a screen, it behaves similarly, moving you +// to the previous screen. If you leave these off, you can't move from +// screen to screen. + +setup_menu_t keys_settings1[] = // Key Binding screen strings +{ + {"MOVEMENT" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y}, + {"FORWARD" ,S_KEY ,m_scrn,KB_X,KB_Y+1*8,{&key_up},&mousebforward}, + {"BACKWARD" ,S_KEY ,m_scrn,KB_X,KB_Y+2*8,{&key_down},&mousebbackward}, + {"TURN LEFT" ,S_KEY ,m_scrn,KB_X,KB_Y+3*8,{&key_left},&mousebturnleft}, + {"TURN RIGHT" ,S_KEY ,m_scrn,KB_X,KB_Y+4*8,{&key_right},&mousebturnright}, + {"RUN" ,S_KEY ,m_scrn,KB_X,KB_Y+5*8,{&key_speed},&mousebspeed,&joybspeed}, + {"STRAFE LEFT" ,S_KEY ,m_scrn,KB_X,KB_Y+6*8,{&key_strafeleft},0,&joybstrafeleft}, + {"STRAFE RIGHT",S_KEY ,m_scrn,KB_X,KB_Y+7*8,{&key_straferight},0,&joybstraferight}, + {"STRAFE" ,S_KEY ,m_scrn,KB_X,KB_Y+8*8,{&key_strafe},&mousebstrafe,&joybstrafe}, + {"AUTORUN" ,S_KEY ,m_scrn,KB_X,KB_Y+9*8,{&key_autorun}}, + {"180 TURN" ,S_KEY ,m_scrn,KB_X,KB_Y+10*8,{&key_reverse}}, + {"USE" ,S_KEY ,m_scrn,KB_X,KB_Y+11*8,{&key_use},&mousebuse,&joybuse}, + {"JUMP/FLY UP" ,S_KEY ,m_scrn,KB_X,KB_Y+12*8,{&key_flyup}}, + {"FLY DOWN" ,S_KEY ,m_scrn,KB_X,KB_Y+13*8,{&key_flydown}}, + {"MOUSE LOOK" ,S_KEY ,m_scrn,KB_X,KB_Y+16*8,{&key_mlook}}, + {"NO VERTICAL MOUSE",S_KEY ,m_scrn,KB_X,KB_Y+17*8,{&key_novert}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {keys_settings2}}, + + // Final entry + {0,S_SKIP|S_END,m_null} + +}; + +setup_menu_t keys_settings2[] = // Key Binding screen strings +{ + {"SCREEN" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y}, + + // phares 4/13/98: + // key_help and key_escape can no longer be rebound. This keeps the + // player from getting themselves in a bind where they can't remember how + // to get to the menus, and can't remember how to get to the help screen + // to give them a clue as to how to get to the menus. :) + + // Also, the keys assigned to these functions cannot be bound to other + // functions. Introduce an S_KEEP flag to show that you cannot swap this + // key with other keys in the same 'group'. (m_scrn, etc.) + + {"HELP" ,S_SKIP|S_KEEP ,m_scrn,0 ,0 ,{&key_help}}, + {"MENU" ,S_SKIP|S_KEEP ,m_scrn,0 ,0 ,{&key_escape}}, + // killough 10/98: hotkey for entering setup menu: + {"SETUP" ,S_KEY ,m_scrn,KB_X,KB_Y+ 1*8,{&key_setup}}, + {"PAUSE" ,S_KEY ,m_scrn,KB_X,KB_Y+ 2*8,{&key_pause}}, + {"AUTOMAP" ,S_KEY ,m_scrn,KB_X,KB_Y+ 3*8,{&key_map}}, + {"VOLUME" ,S_KEY ,m_scrn,KB_X,KB_Y+ 4*8,{&key_soundvolume}}, + {"HUD" ,S_KEY ,m_scrn,KB_X,KB_Y+ 5*8,{&key_hud}}, + {"MESSAGES" ,S_KEY ,m_scrn,KB_X,KB_Y+ 6*8,{&key_messages}}, + {"GAMMA FIX" ,S_KEY ,m_scrn,KB_X,KB_Y+ 7*8,{&key_gamma}}, + {"SPY" ,S_KEY ,m_scrn,KB_X,KB_Y+ 8*8,{&key_spy}}, + {"LARGER VIEW" ,S_KEY ,m_scrn,KB_X,KB_Y+ 9*8,{&key_zoomin}}, + {"SMALLER VIEW",S_KEY ,m_scrn,KB_X,KB_Y+10*8,{&key_zoomout}}, + {"SCREENSHOT" ,S_KEY ,m_scrn,KB_X,KB_Y+11*8,{&key_screenshot}}, + {"GAME" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y+12*8}, + {"SAVE" ,S_KEY ,m_scrn,KB_X,KB_Y+13*8,{&key_savegame}}, + {"LOAD" ,S_KEY ,m_scrn,KB_X,KB_Y+14*8,{&key_loadgame}}, + {"QUICKSAVE" ,S_KEY ,m_scrn,KB_X,KB_Y+15*8,{&key_quicksave}}, + {"QUICKLOAD" ,S_KEY ,m_scrn,KB_X,KB_Y+16*8,{&key_quickload}}, + {"END GAME" ,S_KEY ,m_scrn,KB_X,KB_Y+17*8,{&key_endgame}}, + {"QUIT" ,S_KEY ,m_scrn,KB_X,KB_Y+18*8,{&key_quit}}, + {"<- PREV", S_SKIP|S_PREV,m_null,KB_PREV,KB_Y+20*8, {keys_settings1}}, + {"NEXT ->", S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {keys_settings3}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +setup_menu_t keys_settings3[] = // Key Binding screen strings +{ + {"WEAPONS" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y}, + {"FIST" ,S_KEY ,m_scrn,KB_X,KB_Y+ 1*8,{&key_weapon1},&mb_weapon1}, + {"PISTOL" ,S_KEY ,m_scrn,KB_X,KB_Y+ 2*8,{&key_weapon2},&mb_weapon2}, + {"SHOTGUN" ,S_KEY ,m_scrn,KB_X,KB_Y+ 3*8,{&key_weapon3},&mb_weapon3}, + {"CHAINGUN",S_KEY ,m_scrn,KB_X,KB_Y+ 4*8,{&key_weapon4},&mb_weapon4}, + {"ROCKET" ,S_KEY ,m_scrn,KB_X,KB_Y+ 5*8,{&key_weapon5},&mb_weapon5}, + {"PLASMA" ,S_KEY ,m_scrn,KB_X,KB_Y+ 6*8,{&key_weapon6},&mb_weapon6}, + {"BFG", S_KEY ,m_scrn,KB_X,KB_Y+ 7*8,{&key_weapon7},&mb_weapon7}, + {"CHAINSAW",S_KEY ,m_scrn,KB_X,KB_Y+ 8*8,{&key_weapon8},&mb_weapon8}, + {"SSG" ,S_KEY ,m_scrn,KB_X,KB_Y+ 9*8,{&key_weapon9},&mb_weapon9}, + {"NEXT" ,S_KEY ,m_scrn,KB_X,KB_Y+11*8,{&key_nextweapon}}, + {"PREVIOUS",S_KEY ,m_scrn,KB_X,KB_Y+12*8,{&key_prevweapon}}, + {"BEST" ,S_KEY ,m_scrn,KB_X,KB_Y+13*8,{&key_weapontoggle}}, + {"FIRE" ,S_KEY ,m_scrn,KB_X,KB_Y+15*8,{&key_fire},&mousebfire,&joybfire}, + + {"<- PREV",S_SKIP|S_PREV,m_null,KB_PREV,KB_Y+20*8, {keys_settings2}}, + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {keys_settings4}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} + +}; + +setup_menu_t keys_settings4[] = // Key Binding screen strings +{ + {"AUTOMAP" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y}, + {"FOLLOW" ,S_KEY ,m_map ,KB_X,KB_Y+ 1*8,{&key_map_follow}}, + {"ZOOM IN" ,S_KEY ,m_map ,KB_X,KB_Y+ 2*8,{&key_map_zoomin}}, + {"ZOOM OUT" ,S_KEY ,m_map ,KB_X,KB_Y+ 3*8,{&key_map_zoomout}}, + {"SHIFT UP" ,S_KEY ,m_map ,KB_X,KB_Y+ 4*8,{&key_map_up}}, + {"SHIFT DOWN" ,S_KEY ,m_map ,KB_X,KB_Y+ 5*8,{&key_map_down}}, + {"SHIFT LEFT" ,S_KEY ,m_map ,KB_X,KB_Y+ 6*8,{&key_map_left}}, + {"SHIFT RIGHT",S_KEY ,m_map ,KB_X,KB_Y+ 7*8,{&key_map_right}}, + {"MARK PLACE" ,S_KEY ,m_map ,KB_X,KB_Y+ 8*8,{&key_map_mark}}, + {"CLEAR MARKS",S_KEY ,m_map ,KB_X,KB_Y+ 9*8,{&key_map_clear}}, + {"FULL/ZOOM" ,S_KEY ,m_map ,KB_X,KB_Y+10*8,{&key_map_gobig}}, + {"GRID" ,S_KEY ,m_map ,KB_X,KB_Y+11*8,{&key_map_grid}}, + {"ROTATE" ,S_KEY ,m_map ,KB_X,KB_Y+12*8,{&key_map_rotate}}, + {"OVERLAY" ,S_KEY ,m_map ,KB_X,KB_Y+13*8,{&key_map_overlay}}, +#ifdef GL_DOOM + {"TEXTURED" ,S_KEY ,m_map ,KB_X,KB_Y+14*8,{&key_map_textured}}, +#endif + + {"<- PREV" ,S_SKIP|S_PREV,m_null,KB_PREV,KB_Y+20*8, {keys_settings3}}, + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {keys_settings5}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +setup_menu_t keys_settings5[] = // Key Binding screen strings +{ + {"CHATTING" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y+0*8}, + {"BEGIN CHAT" ,S_KEY ,m_scrn,KB_X,KB_Y+1*8,{&key_chat}}, + {"PLAYER 1" ,S_KEY ,m_scrn,KB_X,KB_Y+2*8,{&destination_keys[0]}}, + {"PLAYER 2" ,S_KEY ,m_scrn,KB_X,KB_Y+3*8,{&destination_keys[1]}}, + {"PLAYER 3" ,S_KEY ,m_scrn,KB_X,KB_Y+4*8,{&destination_keys[2]}}, + {"PLAYER 4" ,S_KEY ,m_scrn,KB_X,KB_Y+5*8,{&destination_keys[3]}}, + {"BACKSPACE" ,S_KEY ,m_scrn,KB_X,KB_Y+6*8,{&key_backspace}}, + {"ENTER" ,S_KEY ,m_scrn,KB_X,KB_Y+7*8,{&key_enter}}, + + {"<- PREV" ,S_SKIP|S_PREV,m_null,KB_PREV,KB_Y+20*8, {keys_settings4}}, + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {keys_settings6}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +//e6y +setup_menu_t keys_settings6[] = // Key Binding screen strings +{ + {"GAME SPEED" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y}, + {"SPEED UP" ,S_KEY ,m_scrn,KB_X,KB_Y+ 1*8,{&key_speed_up}}, + {"SPEED DOWN" ,S_KEY ,m_scrn,KB_X,KB_Y+ 2*8,{&key_speed_down}}, + {"RESET TO DEFAULT" ,S_KEY ,m_scrn,KB_X,KB_Y+ 3*8,{&key_speed_default}}, + {"STEP OF CHANGE (0-AUTO)" ,S_NUM ,m_null,KB_X,KB_Y+ 4*8, {"speed_step"}}, + {"DEMOS" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y+5*8}, + {"START/STOP SKIPPING" ,S_KEY ,m_scrn,KB_X,KB_Y+ 6*8,{&key_demo_skip}}, + {"END LEVEL" ,S_KEY ,m_scrn,KB_X,KB_Y+ 7*8,{&key_demo_endlevel}}, + {"CAMERA MODE" ,S_KEY ,m_scrn,KB_X,KB_Y+ 8*8,{&key_walkcamera}}, + {"JOIN" ,S_KEY ,m_scrn,KB_X,KB_Y+ 9*8,{&key_demo_jointogame}}, + {"MISC" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y+10*8}, + {"RESTART LEVEL/DEMO" ,S_KEY ,m_scrn,KB_X,KB_Y+ 11*8,{&key_level_restart}}, + {"NEXT LEVEL" ,S_KEY ,m_scrn,KB_X,KB_Y+ 12*8,{&key_nextlevel}}, +#ifdef GL_DOOM + {"Show Alive Monsters" ,S_KEY ,m_scrn,KB_X,KB_Y+13*8,{&key_showalive}}, +#endif + + {"<- PREV",S_SKIP|S_PREV,m_null,KB_PREV,KB_Y+20*8, {keys_settings5}}, + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {keys_settings7}}, + // Final entry + {0,S_SKIP|S_END,m_null} +}; + +setup_menu_t keys_settings7[] = +{ + {"MENUS" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y+0*8}, + {"NEXT ITEM" ,S_KEY ,m_menu,KB_X,KB_Y+1*8,{&key_menu_down}}, + {"PREV ITEM" ,S_KEY ,m_menu,KB_X,KB_Y+2*8,{&key_menu_up}}, + {"LEFT" ,S_KEY ,m_menu,KB_X,KB_Y+3*8,{&key_menu_left}}, + {"RIGHT" ,S_KEY ,m_menu,KB_X,KB_Y+4*8,{&key_menu_right}}, + {"BACKSPACE" ,S_KEY ,m_menu,KB_X,KB_Y+5*8,{&key_menu_backspace}}, + {"SELECT ITEM" ,S_KEY ,m_menu,KB_X,KB_Y+6*8,{&key_menu_enter}}, + {"EXIT" ,S_KEY ,m_menu,KB_X,KB_Y+7*8,{&key_menu_escape}}, + {"CLEAR" ,S_KEY ,m_menu,KB_X,KB_Y+8*8,{&key_menu_clear}}, + + {"<- PREV",S_SKIP|S_PREV,m_null,KB_PREV,KB_Y+20*8, {keys_settings6}}, + // Final entry + {0,S_SKIP|S_END,m_null} +}; + +// Setting up for the Key Binding screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_KeyBindings(int choice) +{ + M_SetupNextMenu(&KeybndDef); + + setup_active = true; + setup_screen = ss_keys; + set_keybnd_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = keys_settings[0]; + set_menu_itemon = M_GetSetupMenuItemOn(); + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// The drawing part of the Key Bindings Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawKeybnd(void) +{ + menuactive = mnact_full; + + // Set up the Key Binding screen + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + // proff/nicolas 09/20/98 -- changed for hi-res + M_DrawTitle(84, 2, "M_KEYBND", CR_DEFAULT, "KEY BINDINGS", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + +///////////////////////////// +// +// The Weapon Screen tables. + +#define WP_X 203 +#define WP_Y 33 + +static const char *weapon_attack_alignment_strings[] = { + "OFF", "HORIZONTAL", "CENTERED", "BOBBING", NULL +}; + +// There's only one weapon settings screen (for now). But since we're +// trying to fit a common description for screens, it gets a setup_menu_t, +// which only has one screen definition in it. +// +// Note that this screen has no PREV or NEXT items, since there are no +// neighboring screens. + +enum { // killough 10/98: enum for y-offset info + weap_recoil, + weap_bobbing, + weap_attack_alignment, + weap_stub1, + weap_pref1, + weap_pref2, + weap_pref3, + weap_pref4, + weap_pref5, + weap_pref6, + weap_pref7, + weap_pref8, + weap_pref9, +}; + +setup_menu_t weap_settings1[]; + +setup_menu_t* weap_settings[] = +{ + weap_settings1, + NULL +}; + +setup_menu_t weap_settings1[] = // Weapons Settings screen +{ + {"ENABLE RECOIL", S_YESNO,m_null,WP_X, WP_Y+ weap_recoil*8, {"weapon_recoil"}}, + {"ENABLE BOBBING",S_YESNO,m_null,WP_X, WP_Y+weap_bobbing*8, {"player_bobbing"}}, + {"WEAPON ATTACK ALIGNMENT",S_CHOICE,m_null,WP_X, WP_Y+weap_attack_alignment*8, {"weapon_attack_alignment"}, 0, 0, NULL, weapon_attack_alignment_strings}, + + {"1ST CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref1*8, {"weapon_choice_1"}}, + {"2nd CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref2*8, {"weapon_choice_2"}}, + {"3rd CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref3*8, {"weapon_choice_3"}}, + {"4th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref4*8, {"weapon_choice_4"}}, + {"5th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref5*8, {"weapon_choice_5"}}, + {"6th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref6*8, {"weapon_choice_6"}}, + {"7th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref7*8, {"weapon_choice_7"}}, + {"8th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref8*8, {"weapon_choice_8"}}, + {"9th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref9*8, {"weapon_choice_9"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + // Final entry + {0,S_SKIP|S_END,m_null} + +}; + +// Setting up for the Weapons screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_Weapons(int choice) +{ + M_SetupNextMenu(&WeaponDef); + + setup_active = true; + setup_screen = ss_weap; + set_weapon_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = weap_settings[0]; + set_menu_itemon = M_GetSetupMenuItemOn(); + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + + +// The drawing part of the Weapons Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawWeapons(void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + // proff/nicolas 09/20/98 -- changed for hi-res + M_DrawTitle(109, 2, "M_WEAP", CR_DEFAULT, "WEAPONS", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + +///////////////////////////// +// +// The Status Bar / HUD tables. + +#define SB_X 203 +#define SB_Y 31 + +// Screen table definitions + +setup_menu_t stat_settings1[]; +//e6y +setup_menu_t stat_settings2[]; + +setup_menu_t* stat_settings[] = +{ + stat_settings1, + //e6y + stat_settings2, + NULL +}; + +setup_menu_t stat_settings1[] = // Status Bar and HUD Settings screen +{ + {"STATUS BAR" ,S_SKIP|S_TITLE,m_null,SB_X,SB_Y+ 1*8 }, + + {"USE RED NUMBERS" ,S_YESNO, m_null,SB_X,SB_Y+ 2*8, {"sts_always_red"}}, + {"GRAY %" ,S_YESNO, m_null,SB_X,SB_Y+ 3*8, {"sts_pct_always_gray"}}, + {"SINGLE KEY DISPLAY",S_YESNO, m_null,SB_X,SB_Y+ 4*8, {"sts_traditional_keys"}}, + + {"HEADS-UP DISPLAY" ,S_SKIP|S_TITLE,m_null,SB_X,SB_Y+ 6*8}, + + {"HEALTH LOW/OK" ,S_NUM ,m_null,SB_X,SB_Y+ 7*8, {"health_red"}}, + {"HEALTH OK/GOOD" ,S_NUM ,m_null,SB_X,SB_Y+ 8*8, {"health_yellow"}}, + {"HEALTH GOOD/EXTRA" ,S_NUM ,m_null,SB_X,SB_Y+ 9*8, {"health_green"}}, + {"ARMOR COLOR DEPENDS ON TYPE",S_YESNO, m_null,SB_X,SB_Y+ 10*8, {"sts_armorcolor_type"}}, + {"ARMOR LOW/OK" ,S_NUM ,m_null,SB_X,SB_Y+11*8, {"armor_red"}}, + {"ARMOR OK/GOOD" ,S_NUM ,m_null,SB_X,SB_Y+12*8, {"armor_yellow"}}, + {"ARMOR GOOD/EXTRA" ,S_NUM ,m_null,SB_X,SB_Y+13*8, {"armor_green"}}, + {"AMMO LOW/OK" ,S_NUM ,m_null,SB_X,SB_Y+14*8, {"ammo_red"}}, + {"AMMO OK/GOOD" ,S_NUM ,m_null,SB_X,SB_Y+15*8, {"ammo_yellow"}}, + {"BACKPACK CHANGES THRESHOLDS",S_CHOICE,m_null,SB_X,SB_Y+16*8, + {"ammo_colour_behaviour"},0,0,NULL,ammo_colour_behaviour_list}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + //e6y + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,SB_Y+20*8, {stat_settings2}}, + // Final entry + {0,S_SKIP|S_END,m_null} +}; + +//e6y +#define ADVHUD_X 284 +setup_menu_t stat_settings2[] = +{ + {"ADVANCED HUD SETTINGS" ,S_SKIP|S_TITLE,m_null,ADVHUD_X,SB_Y+1*8}, + {"REPORT REVEALED SECRETS" ,S_YESNO ,m_null,ADVHUD_X,SB_Y+ 2*8, {"hudadd_secretarea"}}, + {"SMART TOTALS" ,S_YESNO ,m_null,ADVHUD_X,SB_Y+ 3*8, {"hudadd_smarttotals"}}, + {"SHOW GAMESPEED" ,S_YESNO ,m_null,ADVHUD_X,SB_Y+ 4*8, {"hudadd_gamespeed"}}, + {"SHOW LEVELTIME" ,S_YESNO ,m_null,ADVHUD_X,SB_Y+ 5*8, {"hudadd_leveltime"}}, + {"SHOW DEMOTIME" ,S_YESNO ,m_null,ADVHUD_X,SB_Y+ 6*8, {"hudadd_demotime"}}, + {"SHOW PROGRESS BAR DURING DEMO PLAYBACK" ,S_YESNO ,m_null,ADVHUD_X,SB_Y+ 7*8, {"hudadd_demoprogressbar"}}, + {"SHOW TIME/STS ABOVE STATUS BAR" ,S_YESNO ,m_null,ADVHUD_X,SB_Y+ 8*8, {"hudadd_timests"}}, + + {"CROSSHAIR SETTINGS" ,S_SKIP|S_TITLE,m_null,ADVHUD_X,SB_Y+10*8}, + {"ENABLE CROSSHAIR" ,S_CHOICE ,m_null,ADVHUD_X,SB_Y+11*8, {"hudadd_crosshair"}, 0, 0, 0, crosshair_str}, + {"SCALE CROSSHAIR" ,S_YESNO ,m_null,ADVHUD_X,SB_Y+12*8, {"hudadd_crosshair_scale"}}, + {"CHANGE CROSSHAIR COLOR BY PLAYER HEALTH" ,S_YESNO ,m_null,ADVHUD_X,SB_Y+13*8, {"hudadd_crosshair_health"}}, + {"CHANGE CROSSHAIR COLOR ON TARGET" ,S_YESNO ,m_null,ADVHUD_X,SB_Y+14*8, {"hudadd_crosshair_target"}}, + {"LOCK CROSSHAIR ON TARGET" ,S_YESNO ,m_null,ADVHUD_X,SB_Y+15*8, {"hudadd_crosshair_lock_target"}}, + {"DEFAULT CROSSHAIR COLOR" ,S_CRITEM ,m_null,ADVHUD_X,SB_Y+16*8, {"hudadd_crosshair_color"}}, + {"TARGET CROSSHAIR COLOR" ,S_CRITEM ,m_null,ADVHUD_X,SB_Y+17*8, {"hudadd_crosshair_target_color"}}, + + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + {"<- PREV",S_SKIP|S_PREV,m_null,KB_PREV,SB_Y+20*8, {stat_settings1}}, + {0,S_SKIP|S_END,m_null} +}; + +// Setting up for the Status Bar / HUD screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_StatusBar(int choice) +{ + M_SetupNextMenu(&StatusHUDDef); + + setup_active = true; + setup_screen = ss_stat; + set_status_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = stat_settings[0]; + set_menu_itemon = M_GetSetupMenuItemOn(); + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + + +// The drawing part of the Status Bar / HUD Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawStatusHUD(void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + // proff/nicolas 09/20/98 -- changed for hi-res + M_DrawTitle(59, 2, "M_STAT", CR_DEFAULT, "STATUS BAR / HUD", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + + +///////////////////////////// +// +// The Automap tables. + +#define AU_X 275 +#define AU_Y 31 +#define AU_PREV KB_PREV +#define AU_NEXT KB_NEXT + +setup_menu_t auto_settings1[]; +setup_menu_t auto_settings2[]; +setup_menu_t auto_settings3[]; + +setup_menu_t* auto_settings[] = +{ + auto_settings1, + auto_settings2, + auto_settings3, + NULL +}; + +setup_menu_t auto_settings1[] = // 1st AutoMap Settings screen +{ + {"Show Kills/Secrts/Items statistics", S_YESNO,m_null,AU_X,AU_Y+0*8, {"map_level_stat"}}, + {"Show coordinates of automap pointer", S_YESNO,m_null,AU_X,AU_Y+1*8, {"map_point_coord"}}, // killough 10/98 + {"Show Secrets only after entering", S_YESNO,m_null,AU_X,AU_Y+2*8, {"map_secret_after"}}, + {"Update unexplored parts in automap mode", S_YESNO,m_null,AU_X,AU_Y+3*8, {"map_always_updates"}}, + {"Grid cell size 8..256, -1 for autosize", S_NUM, m_null,AU_X,AU_Y+4*8, {"map_grid_size"}, 0, 0, M_ChangeMapGridSize}, + {"Scroll / Zoom speed (1..32)", S_NUM, m_null,AU_X,AU_Y+5*8, {"map_scroll_speed"}}, + {"Use mouse wheel for zooming", S_YESNO,m_null,AU_X,AU_Y+6*8, {"map_wheel_zoom"}}, + {"Apply multisampling", S_YESNO,m_null,AU_X,AU_Y+7*8, {"map_use_multisamling"}, 0, 0, M_ChangeMapMultisamling}, +#ifdef GL_DOOM + {"Enable textured display", S_YESNO,m_null,AU_X,AU_Y+8*8, {"map_textured"}, 0, 0, M_ChangeMapTextured}, + {"Things appearance", S_CHOICE,m_null,AU_X,AU_Y+9*8, {"map_things_appearance"}, 0, 0, NULL, map_things_appearance_list}, + {"Translucency percentage", S_SKIP|S_TITLE,m_null,AU_X,AU_Y+10*8}, + {"Textured automap", S_NUM, m_null,AU_X,AU_Y+11*8, {"map_textured_trans"}}, + {"Textured automap in overlay mode", S_NUM, m_null,AU_X,AU_Y+12*8, {"map_textured_overlay_trans"}}, + {"Lines in overlay mode", S_NUM, m_null,AU_X,AU_Y+13*8, {"map_lines_overlay_trans"}}, +#endif + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + {"NEXT ->",S_SKIP|S_NEXT,m_null,AU_NEXT,AU_Y+20*8, {auto_settings2}}, + + // Final entry + {0,S_SKIP|S_END,m_null} +}; + +setup_menu_t auto_settings2[] = // 2st AutoMap Settings screen +{ + {"background", S_COLOR, m_null, AU_X, AU_Y, {"mapcolor_back"}}, + {"grid lines", S_COLOR, m_null, AU_X, AU_Y + 1*8, {"mapcolor_grid"}}, + {"normal 1s wall", S_COLOR, m_null,AU_X,AU_Y+ 2*8, {"mapcolor_wall"}}, + {"line at floor height change", S_COLOR, m_null, AU_X, AU_Y+ 3*8, {"mapcolor_fchg"}}, + {"line at ceiling height change" ,S_COLOR,m_null,AU_X,AU_Y+ 4*8, {"mapcolor_cchg"}}, + {"line at sector with floor = ceiling",S_COLOR,m_null,AU_X,AU_Y+ 5*8, {"mapcolor_clsd"}}, + {"red key" ,S_COLOR,m_null,AU_X,AU_Y+ 6*8, {"mapcolor_rkey"}}, + {"blue key" ,S_COLOR,m_null,AU_X,AU_Y+ 7*8, {"mapcolor_bkey"}}, + {"yellow key" ,S_COLOR,m_null,AU_X,AU_Y+ 8*8, {"mapcolor_ykey"}}, + {"red door" ,S_COLOR,m_null,AU_X,AU_Y+ 9*8, {"mapcolor_rdor"}}, + {"blue door" ,S_COLOR,m_null,AU_X,AU_Y+10*8, {"mapcolor_bdor"}}, + {"yellow door" ,S_COLOR,m_null,AU_X,AU_Y+11*8, {"mapcolor_ydor"}}, + + {"AUTOMAP LEVEL TITLE COLOR" ,S_CRITEM,m_null,AU_X,AU_Y+12*8, {"hudcolor_titl"}}, + {"AUTOMAP COORDINATES COLOR" ,S_CRITEM,m_null,AU_X,AU_Y+13*8, {"hudcolor_xyco"}}, + + {"Automap Statistics Titles Color",S_CRITEM,m_null,AU_X,AU_Y+14*8, {"hudcolor_mapstat_title"}}, + {"Automap Statistics Values Color",S_CRITEM,m_null,AU_X,AU_Y+15*8, {"hudcolor_mapstat_value"}}, + {"Automap Statistics Time Color" ,S_CRITEM,m_null,AU_X,AU_Y+16*8, {"hudcolor_mapstat_time"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + {"<- PREV",S_SKIP|S_PREV,m_null,AU_PREV,AU_Y+20*8, {auto_settings1}}, + {"NEXT ->",S_SKIP|S_NEXT,m_null,AU_NEXT,AU_Y+20*8, {auto_settings3}}, + + // Final entry + {0,S_SKIP|S_END,m_null} + +}; + +setup_menu_t auto_settings3[] = // 3nd AutoMap Settings screen +{ + {"teleporter line" ,S_COLOR ,m_null,AU_X,AU_Y, {"mapcolor_tele"}}, + {"secret sector boundary" ,S_COLOR ,m_null,AU_X,AU_Y+ 1*8, {"mapcolor_secr"}}, + //jff 4/23/98 add exit line to automap + {"exit line" ,S_COLOR ,m_null,AU_X,AU_Y+ 2*8, {"mapcolor_exit"}}, + {"computer map unseen line" ,S_COLOR ,m_null,AU_X,AU_Y+ 3*8, {"mapcolor_unsn"}}, + {"line w/no floor/ceiling changes",S_COLOR ,m_null,AU_X,AU_Y+ 4*8, {"mapcolor_flat"}}, + {"general sprite" ,S_COLOR ,m_null,AU_X,AU_Y+ 5*8, {"mapcolor_sprt"}}, + {"countable enemy sprite" ,S_COLOR ,m_null,AU_X,AU_Y+ 6*8, {"mapcolor_enemy"}}, // cph 2006/06/30 + {"countable item sprite" ,S_COLOR ,m_null,AU_X,AU_Y+ 7*8, {"mapcolor_item"}}, // mead 3/4/2003 + {"crosshair" ,S_COLOR ,m_null,AU_X,AU_Y+ 8*8, {"mapcolor_hair"}}, + {"single player arrow" ,S_COLOR ,m_null,AU_X,AU_Y+ 9*8, {"mapcolor_sngl"}}, + {"your colour in multiplayer" ,S_COLOR ,m_null,AU_X,AU_Y+10*8, {"mapcolor_me"}}, + + {"friends" ,S_COLOR ,m_null,AU_X,AU_Y+12*8, {"mapcolor_frnd"}}, // killough 8/8/98 + + {"<- PREV",S_SKIP|S_PREV,m_null,AU_PREV,AU_Y+20*8, {auto_settings2}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} + +}; + + +// Setting up for the Automap screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_Automap(int choice) +{ + M_SetupNextMenu(&AutoMapDef); + + setup_active = true; + setup_screen = ss_auto; + set_auto_active = true; + setup_select = false; + colorbox_active = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = auto_settings[0]; + set_menu_itemon = M_GetSetupMenuItemOn(); + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// Data used by the color palette that is displayed for the player to +// select colors. + +int color_palette_x; // X position of the cursor on the color palette +int color_palette_y; // Y position of the cursor on the color palette +byte palette_background[16*(CHIP_SIZE+1)+8]; + +// M_DrawColPal() draws the color palette when the user needs to select a +// color. + +// phares 4/1/98: now uses a single lump for the palette instead of +// building the image out of individual paint chips. + +static void M_DrawColPal(void) +{ + int cpx, cpy; + + // Draw a background, border, and paint chips + + // proff/nicolas 09/20/98 -- changed for hi-res + // CPhipps - patch drawing updated + V_DrawNamePatch(COLORPALXORIG-5, COLORPALYORIG-5, 0, "M_COLORS", CR_DEFAULT, VPT_STRETCH); + + // Draw the cursor around the paint chip + // (cpx,cpy) is the upper left-hand corner of the paint chip + + cpx = COLORPALXORIG+color_palette_x*(CHIP_SIZE+1)-1; + cpy = COLORPALYORIG+color_palette_y*(CHIP_SIZE+1)-1; + // proff 12/6/98: Drawing of colorchips completly changed for hi-res, it now uses a patch + V_DrawNamePatch(cpx,cpy,0,"M_PALSEL",CR_DEFAULT,VPT_STRETCH); // PROFF_GL_FIX +} + +// The drawing part of the Automap Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawAutoMap(void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + // CPhipps - patch drawing updated + M_DrawTitle(109, 2, "M_AUTO", CR_DEFAULT, "AUTOMAP", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If a color is being selected, need to show color paint chips + + if (colorbox_active) + M_DrawColPal(); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + else if (default_verify) + M_DrawDefVerify(); +} + + +///////////////////////////// +// +// The Enemies table. + +#define E_X 250 +#define E_Y 31 + +setup_menu_t enem_settings1[]; + +setup_menu_t* enem_settings[] = +{ + enem_settings1, + NULL +}; + +enum { + enem_infighting, + + enem_remember = 1, + + enem_backing, + enem_monkeys, + enem_avoid_hazards, + enem_friction, + enem_help_friends, + + enem_helpers, + + enem_distfriend, + + enem_dog_jumping, + + enem_colored_blood, + + enem_end +}; + +setup_menu_t enem_settings1[] = // Enemy Settings screen +{ + // killough 7/19/98 + {"Monster Infighting When Provoked",S_YESNO,m_null,E_X,E_Y+ enem_infighting*8, {"monster_infighting"}}, + + {"Remember Previous Enemy",S_YESNO,m_null,E_X,E_Y+ enem_remember*8, {"monsters_remember"}}, + + // killough 9/8/98 + {"Monster Backing Out",S_YESNO,m_null,E_X,E_Y+ enem_backing*8, {"monster_backing"}}, + + {"Climb Steep Stairs", S_YESNO,m_null,E_X,E_Y+enem_monkeys*8, {"monkeys"}}, + + // killough 9/9/98 + {"Intelligently Avoid Hazards",S_YESNO,m_null,E_X,E_Y+ enem_avoid_hazards*8, {"monster_avoid_hazards"}}, + + // killough 10/98 + {"Affected by Friction",S_YESNO,m_null,E_X,E_Y+ enem_friction*8, {"monster_friction"}}, + + {"Rescue Dying Friends",S_YESNO,m_null,E_X,E_Y+ enem_help_friends*8, {"help_friends"}}, + + // killough 7/19/98 + {"Number Of Single-Player Helper Dogs",S_NUM|S_LEVWARN,m_null,E_X,E_Y+ enem_helpers*8, {"player_helpers"}}, + + // killough 8/8/98 + {"Distance Friends Stay Away",S_NUM,m_null,E_X,E_Y+ enem_distfriend*8, {"friend_distance"}}, + + {"Allow dogs to jump down",S_YESNO,m_null,E_X,E_Y+ enem_dog_jumping*8, {"dog_jumping"}}, + + {"Colored blood and gibs",S_YESNO,m_null,E_X,E_Y+ enem_colored_blood*8, {"colored_blood"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + // Final entry + {0,S_SKIP|S_END,m_null} + +}; + +///////////////////////////// + +// Setting up for the Enemies screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_Enemy(int choice) +{ + M_SetupNextMenu(&EnemyDef); + + setup_active = true; + setup_screen = ss_enem; + set_enemy_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = enem_settings[0]; + set_menu_itemon = M_GetSetupMenuItemOn(); + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// The drawing part of the Enemies Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawEnemy(void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + // proff/nicolas 09/20/98 -- changed for hi-res + M_DrawTitle(114, 2, "M_ENEM", CR_DEFAULT, "ENEMIES", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + + +///////////////////////////// +// +// The General table. +// killough 10/10/98 + +extern int usejoystick, usemouse, default_mus_card, default_snd_card; +extern int detect_voices, realtic_clock_rate, tran_filter_pct; + +setup_menu_t gen_settings1[], gen_settings2[], gen_settings3[]; +setup_menu_t gen_settings4[], gen_settings5[], gen_settings6[]; +setup_menu_t gen_settings7[], gen_settings8[]; + +setup_menu_t* gen_settings[] = +{ + gen_settings1, + gen_settings2, + gen_settings3, + gen_settings4, + gen_settings5, + gen_settings6, + gen_settings7, + gen_settings8, + NULL +}; + +#define G_X 226 +#define GF_X 76 +#define G_Y 23 +#define G_X2 284 + +static const char *videomodes[] = { + "8bit","15bit","16bit", "32bit", +#ifdef GL_DOOM + "OpenGL", +#endif + NULL}; + +static const char *gltexformats[] = { + "GL_RGBA","GL_RGB5_A1", "GL_RGBA4", NULL}; + +setup_menu_t gen_settings1[] = { // General Settings screen1 + + {"Video", S_SKIP|S_TITLE, m_null, G_X, G_Y+ 1*8}, + {"Video mode", S_CHOICE, m_null, G_X, G_Y+ 2*8, {"videomode"}, 0, 0, M_ChangeVideoMode, videomodes}, + {"Screen Resolution", S_CHOICE, m_null, G_X, G_Y+ 3*8, {"screen_resolution"}, 0, 0, M_ChangeVideoMode, screen_resolutions_list}, + {"Aspect Ratio", S_CHOICE, m_null, G_X, G_Y+ 4*8, {"render_aspect"}, 0, 0, M_ChangeAspectRatio, render_aspects_list}, + {"Fullscreen Video mode", S_YESNO, m_null, G_X, G_Y+ 5*8, {"use_fullscreen"}, 0, 0, M_ChangeFullScreen}, + {"Status Bar and Menu Appearance", S_CHOICE, m_null, G_X, G_Y+ 6*8, {"render_stretch_hud"}, 0, 0, M_ChangeStretch, render_stretch_list}, + {"Vertical Sync", S_YESNO, m_null, G_X, G_Y+ 7*8, {"render_vsync"}, 0, 0, M_ChangeVideoMode}, + + {"Enable Translucency", S_YESNO, m_null, G_X, G_Y+ 9*8, {"translucency"}, 0, 0, M_Trans}, + {"Translucency filter percentage", S_NUM, m_null, G_X, G_Y+10*8, {"tran_filter_pct"}, 0, 0, M_Trans}, + {"Uncapped Framerate", S_YESNO, m_null, G_X, G_Y+11*8, {"uncapped_framerate"}, 0, 0, M_ChangeUncappedFrameRate}, + + {"Sound & Music", S_SKIP|S_TITLE, m_null, G_X, G_Y+13*8}, + {"Number of Sound Channels", S_NUM|S_PRGWARN, m_null, G_X, G_Y+14*8, {"snd_channels"}}, + {"Enable v1.1 Pitch Effects", S_YESNO, m_null, G_X, G_Y+15*8, {"pitched_sounds"}}, + {"PC Speaker emulation", S_YESNO|S_PRGWARN, m_null, G_X, G_Y+16*8, {"snd_pcspeaker"}}, + {"Preferred MIDI player", S_CHOICE|S_PRGWARN, m_null, G_X, G_Y+17*8, {"snd_midiplayer"}, 0, 0, M_ChangeMIDIPlayer, midiplayers}, + {"disable sound cutoffs", S_YESNO, m_null, G_X, G_Y+18*8, {"full_sounds"}}, +//{"Low-pass filter", S_YESNO, m_null, G_X, G_Y+19*8, {"lowpass_filter"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {gen_settings2}}, + {0,S_SKIP|S_END,m_null} +}; + +static const char *gen_skillstrings[] = { + // Dummy first option because defaultskill is 1-based + "", "ITYTD", "HNTR", "HMP", "UV", "NM", NULL +}; + +static const char *gen_compstrings[] = +{ + "17/Default", + "0/Doom v1.2", + "1/Doom v1.666", + "2/Doom (2) v1.9", + "3/Ultimate v1.9", + "4/Final Doom", + "5/DosDoom", + "6/TASDoom", + "7/Boom vanilla", + "8/Boom v2.01", + "9/Boom", + "10/LxDoom", + "11/MBF", + "12/PrB 2.03b", + "13/PrB 2.1.x", + "14/PrB 2.2.x", + "15/PrB 2.3.x", + "16/PrB 2.4.0", + "17/PrB+ Latest", + NULL +}; + +setup_menu_t gen_settings2[] = { // General Settings screen2 + + {"Input Devices", S_SKIP|S_TITLE, m_null, G_X, G_Y+ 1*8}, + {"Enable Mouse", S_YESNO, m_null, G_X, G_Y+ 2*8, {"use_mouse"}}, + {"Enable Joystick", S_YESNO, m_null, G_X, G_Y+ 3*8, {"use_joystick"}}, + + {"Files Preloaded at Game Startup", S_SKIP|S_TITLE, m_null, G_X, G_Y + 5*8}, + {"WAD # 1", S_FILE, m_null, GF_X, G_Y+ 6*8, {"wadfile_1"}}, + {"WAD #2", S_FILE, m_null, GF_X, G_Y+ 7*8, {"wadfile_2"}}, + {"DEH/BEX # 1", S_FILE, m_null, GF_X, G_Y+ 8*8, {"dehfile_1"}}, + {"DEH/BEX #2", S_FILE, m_null, GF_X, G_Y+ 9*8, {"dehfile_2"}}, + + {"Miscellaneous", S_SKIP|S_TITLE, m_null, G_X, G_Y+11*8}, + {"Maximum number of player corpses", S_NUM|S_PRGWARN, m_null, G_X, G_Y+12*8, {"max_player_corpse"}}, + {"Game speed, percentage of normal", S_NUM|S_PRGWARN, m_null, G_X, G_Y+13*8, {"realtic_clock_rate"}}, + {"Default skill level", S_CHOICE, m_null, G_X, G_Y+14*8, {"default_skill"}, 0, 0, NULL, gen_skillstrings}, + {"Default compatibility level", S_CHOICE, m_null, G_X, G_Y+15*8, {"default_compatibility_level"}, 0, 0, NULL, &gen_compstrings[1]}, + {"Show ENDOOM screen", S_YESNO, m_null, G_X, G_Y+16*8, {"showendoom"}}, + {"Fullscreen menu background", S_YESNO, m_null, G_X, G_Y + 17*8, {"menu_background"}}, +#ifdef USE_WINDOWS_LAUNCHER + {"Use In-Game Launcher", S_CHOICE, m_null, G_X, G_Y+ 18*8, {"launcher_enable"}, 0, 0, NULL, launcher_enable_states}, +#endif + + + {"<- PREV",S_SKIP|S_PREV, m_null, KB_PREV, KB_Y+20*8, {gen_settings1}}, + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {gen_settings3}}, + {0,S_SKIP|S_END,m_null} +}; + +setup_menu_t gen_settings3[] = { // General Settings screen2 + {"Demos", S_SKIP|S_TITLE, m_null, G_X, G_Y+ 1*8}, + {"Use Extended Format", S_YESNO|S_PRGWARN, m_null,G_X,G_Y+ 2*8, {"demo_extendedformat"}, 0, 0, M_ChangeDemoExtendedFormat}, + {"Overwrite Existing", S_YESNO, m_null, G_X, G_Y+ 3*8, {"demo_overwriteexisting"}}, + {"Smooth Demo Playback", S_YESNO, m_null, G_X, G_Y+ 4*8, {"demo_smoothturns"}, 0, 0, M_ChangeDemoSmoothTurns}, + {"Smooth Demo Playback Factor", S_NUM, m_null, G_X, G_Y+ 5*8, {"demo_smoothturnsfactor"}, 0, 0, M_ChangeDemoSmoothTurns}, + {"Quickstart Window (ms)", S_NUM, m_null, G_X, G_Y+6*8, {"quickstart_window_ms"}}, + + {"Movements", S_SKIP|S_TITLE,m_null,G_X, G_Y+8*8}, + {"Permanent Strafe50", S_YESNO, m_null, G_X, G_Y+ 9*8, {"movement_strafe50"}, 0, 0, M_ChangeSpeed}, + + {"Mouse", S_SKIP|S_TITLE,m_null, G_X, G_Y+11*8}, + {"Dbl-Click As Use", S_YESNO, m_null, G_X, G_Y+12*8, {"mouse_doubleclick_as_use"}}, + {"Carry Fractional Tics", S_YESNO, m_null, G_X, G_Y+13*8, {"mouse_carrytics"}}, + {"Enable Mouselook", S_YESNO, m_null, G_X, G_Y+14*8, {"movement_mouselook"}, 0, 0, M_ChangeMouseLook}, + {"No Vertical Mouse", S_YESNO, m_null, G_X, G_Y+15*8, {"movement_mousenovert"}}, + {"Invert Mouse", S_YESNO, m_null, G_X, G_Y+16*8, {"movement_mouseinvert"}, 0, 0, M_ChangeMouseInvert}, + {"Max View Pitch", S_NUM, m_null, G_X, G_Y+17*8, {"movement_maxviewpitch"}, 0, 0, M_ChangeMaxViewPitch}, + {"Mouse Strafe Divisor", S_NUM, m_null, G_X, G_Y+18*8, {"movement_mousestrafedivisor"}}, + + {"<- PREV",S_SKIP|S_PREV, m_null,KB_PREV, KB_Y+20*8, {gen_settings2}}, + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {gen_settings4}}, + {0,S_SKIP|S_END,m_null} +}; + +static const char *renderfilters[] = {"none", "point", "linear", "rounded"}; +static const char *edgetypes[] = {"jagged", "sloped"}; + +setup_menu_t gen_settings4[] = { // General Settings screen3 + {"Display Options", S_SKIP|S_TITLE, m_null, G_X, G_Y+ 1*8}, + {"Filter for walls", S_CHOICE, m_null, G_X, G_Y+ 2*8, {"filter_wall"}, 0, 0, NULL, renderfilters}, + {"Filter for floors/ceilings", S_CHOICE, m_null, G_X, G_Y+ 3*8, {"filter_floor"}, 0, 0, NULL, renderfilters}, + {"Filter for sprites", S_CHOICE, m_null, G_X, G_Y+ 4*8, {"filter_sprite"}, 0, 0, NULL, renderfilters}, + {"Filter for patches", S_CHOICE, m_null, G_X, G_Y+ 5*8, {"filter_patch"}, 0, 0, NULL, renderfilters}, + {"Filter for lighting", S_CHOICE, m_null, G_X, G_Y+ 6*8, {"filter_z"}, 0, 0, NULL, renderfilters}, + + {"Drawing of sprite edges", S_CHOICE, m_null, G_X, G_Y+ 8*8, {"sprite_edges"}, 0, 0, NULL, edgetypes}, + {"Drawing of patch edges", S_CHOICE, m_null, G_X, G_Y+ 9*8, {"patch_edges"}, 0, 0, NULL, edgetypes}, + {"Flashing HOM indicator", S_YESNO, m_null, G_X, G_Y+10*8, {"flashing_hom"}}, + + // prboom-plus + {"Wipe Screen Effect", S_YESNO, m_null, G_X, G_Y+12*8, {"render_wipescreen"}}, + {"Change Palette On Pain", S_YESNO, m_null, G_X, G_Y+14*8, {"palette_ondamage"}, 0, 0, M_ChangeApplyPalette}, + {"Change Palette On Bonus", S_YESNO, m_null, G_X, G_Y+15*8, {"palette_onbonus"}, 0, 0, M_ChangeApplyPalette}, + {"Change Palette On Powers", S_YESNO, m_null, G_X, G_Y+16*8, {"palette_onpowers"}, 0, 0, M_ChangeApplyPalette}, + + {"<- PREV",S_SKIP|S_PREV, m_null, KB_PREV, KB_Y+20*8, {gen_settings3}}, + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {gen_settings5}}, + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +setup_menu_t gen_settings5[] = { // General Settings screen3 + {"Software Options", S_SKIP|S_TITLE, m_null, G_X, G_Y+1*8}, + {"Screen Multiple Factor (1-None)", S_NUM,m_null,G_X,G_Y+2*8, {"render_screen_multiply"}, 0, 0, M_ChangeScreenMultipleFactor}, + {"Integer Screen Scaling", S_YESNO, m_null, G_X, G_Y+3*8, {"integer_scaling"}, 0, 0, M_ChangeScreenMultipleFactor}, +#ifdef GL_DOOM + {"OpenGL Options", S_SKIP|S_TITLE,m_null,G_X,G_Y+5*8}, + {"Multisampling (0-None)", S_NUM|S_PRGWARN|S_CANT_GL_ARB_MULTISAMPLEFACTOR,m_null,G_X,G_Y+6*8, {"render_multisampling"}, 0, 0, M_ChangeMultiSample}, + {"Field Of View", S_NUM, m_null, G_X, G_Y+ 7*8, {"render_fov"}, 0, 0, M_ChangeFOV}, + {"Sector Light Mode", S_CHOICE, m_null, G_X, G_Y+ 8*8, {"gl_lightmode"}, 0, 0, M_ChangeLightMode, gl_lightmodes}, + {"Allow Fog", S_YESNO, m_null, G_X, G_Y+ 9*8, {"gl_fog"}, 0, 0, M_ChangeAllowFog}, + {"Simple Shadows", S_YESNO, m_null, G_X, G_Y+10*8, {"gl_shadows"}}, + {"Thing Sprite Fuzz", S_CHOICE, m_null, G_X, G_Y+11*8, {"gl_thingspritefuzzmode"}, 0, 0, 0, gl_spritefuzzmodes}, + {"Weapon Sprite Fuzz", S_CHOICE, m_null, G_X, G_Y+12*8, {"gl_weaponspritefuzzmode"}, 0, 0, 0, gl_spritefuzzmodes}, + + {"Paper Items", S_YESNO, m_null, G_X, G_Y+13*8, {"render_paperitems"}}, + {"Smooth sprite edges", S_YESNO, m_null, G_X, G_Y+14*8, {"gl_sprite_blend"}}, + {"Adjust Sprite Clipping", S_CHOICE, m_null, G_X, G_Y+15*8, {"gl_spriteclip"}, 0, 0, M_ChangeSpriteClip, gl_spriteclipmodes}, + {"Item out of Floor offset", S_NUM, m_null, G_X, G_Y+16*8, {"gl_sprite_offset"}, 0, 0, M_ChangeSpriteClip}, + {"Health Bar Above Monsters", S_YESNO, m_null, G_X, G_Y+17*8, {"health_bar"}}, +#endif + + {"<- PREV",S_SKIP|S_PREV, m_null,KB_PREV, KB_Y+20*8, {gen_settings4}}, + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {gen_settings6}}, + {0,S_SKIP|S_END,m_null} +}; + +setup_menu_t gen_settings6[] = +{ + {"EMULATION" ,S_SKIP|S_TITLE,m_null,G_X2,G_Y+ 1*8}, + {"WARN ON SPECHITS OVERFLOW" ,S_YESNO ,m_null,G_X2,G_Y+ 2*8, {"overrun_spechit_warn"}}, + {"TRY TO EMULATE IT" ,S_YESNO ,m_null,G_X2,G_Y+ 3*8, {"overrun_spechit_emulate"}}, + {"WARN ON REJECT OVERFLOW" ,S_YESNO ,m_null,G_X2,G_Y+ 4*8, {"overrun_reject_warn"}}, + {"TRY TO EMULATE IT" ,S_YESNO ,m_null,G_X2,G_Y+ 5*8, {"overrun_reject_emulate"}}, + {"WARN ON INTERCEPTS OVERFLOW" ,S_YESNO ,m_null,G_X2,G_Y+ 6*8, {"overrun_intercept_warn"}}, + {"TRY TO EMULATE IT" ,S_YESNO ,m_null,G_X2,G_Y+ 7*8, {"overrun_intercept_emulate"}}, + {"WARN ON PLAYERINGAME OVERFLOW" ,S_YESNO ,m_null,G_X2,G_Y+ 8*8, {"overrun_playeringame_warn"}}, + {"TRY TO EMULATE IT" ,S_YESNO ,m_null,G_X2,G_Y+ 9*8, {"overrun_playeringame_emulate"}}, + {"WARN ON DONUT OVERFLOW" ,S_YESNO ,m_null,G_X2,G_Y+10*8, {"overrun_donut_warn"}}, + {"TRY TO EMULATE IT" ,S_YESNO ,m_null,G_X2,G_Y+11*8, {"overrun_donut_emulate"}}, + {"WARN ON MISSEDBACKSIDE OVERFLOW" ,S_YESNO ,m_null,G_X2,G_Y+12*8, {"overrun_missedbackside_warn"}}, + {"TRY TO EMULATE IT" ,S_YESNO ,m_null,G_X2,G_Y+13*8, {"overrun_missedbackside_emulate"}}, + + {"<- PREV",S_SKIP|S_PREV,m_null,KB_PREV,KB_Y+20*8, {gen_settings5}}, + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {gen_settings7}}, + {0,S_SKIP|S_END,m_null} +}; + +static const char *jumpheights[] = { + "No", "Low", "High", NULL}; + +setup_menu_t gen_settings7[] = +{ + {"COMPATIBILITY WITH COMMON MAPPING ERRORS" ,S_SKIP|S_TITLE,m_null,G_X2,G_Y+1*8}, + {"LINEDEFS W/O TAGS APPLY LOCALLY" ,S_YESNO ,m_null,G_X2,G_Y+2*8, {"comperr_zerotag"}}, + {"USE PASSES THRU ALL SPECIAL LINES" ,S_YESNO ,m_null,G_X2,G_Y+3*8, {"comperr_passuse"}}, + {"WALK UNDER SOLID HANGING BODIES" ,S_YESNO ,m_null,G_X2,G_Y+4*8, {"comperr_hangsolid"}}, + {"FIX CLIPPING PROBLEMS IN LARGE LEVELS" ,S_YESNO ,m_null,G_X2,G_Y+5*8, {"comperr_blockmap"}}, + {"ALLOW JUMP" ,S_CHOICE ,m_null,G_X2,G_Y+6*8, {"comperr_allowjump"}, 0, 0, NULL, jumpheights}, + {"ALLOW VERTICAL AIMING" ,S_YESNO ,m_null,G_X2,G_Y+7*8, {"comperr_freeaim"}}, + + {"<- PREV",S_SKIP|S_PREV,m_null,KB_PREV,KB_Y+20*8, {gen_settings6}}, +#ifdef GL_DOOM + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {gen_settings8}}, +#endif + {0,S_SKIP|S_END,m_null} +}; + +static const char *gltexfilters[] = { + "None", "Linear", "Nearest Mipmap", "Linear Mipmap", "Bilinear", "Trilinear", NULL}; + +static const char *gltexfilters_anisotropics[] = + {"Off", "2x", "4x", "8x", "16x", NULL}; + +setup_menu_t gen_settings8[] = { // General Settings screen4 +#ifdef GL_DOOM + {"Texture Options", S_SKIP|S_TITLE,m_null,G_X,G_Y+ 1*8}, + {"Texture Filter Mode", S_CHOICE, m_null, G_X, G_Y+2 *8, {"gl_texture_filter"}, 0, 0, M_ChangeTextureParams, gltexfilters}, + {"Sprite Filter Mode", S_CHOICE, m_null, G_X, G_Y+3 *8, {"gl_sprite_filter"}, 0, 0, M_ChangeTextureParams, gltexfilters}, + {"Patch Filter Mode", S_CHOICE, m_null, G_X, G_Y+4 *8, {"gl_patch_filter"}, 0, 0, M_ChangeTextureParams, gltexfilters}, + {"Anisotropic filter", S_CHOICE, m_null, G_X, G_Y+5 *8, {"gl_texture_filter_anisotropic"}, 0, 0, M_ChangeTextureParams, gltexfilters_anisotropics}, + {"Texture format", S_CHOICE, m_null, G_X, G_Y+6 *8, {"gl_tex_format_string"}, 0, 0, M_ChangeTextureParams, gltexformats}, + + {"Enable Colormaps", S_YESNO, m_null, G_X,G_Y+ 8*8, {"gl_boom_colormaps"}, 0, 0, M_ChangeAllowBoomColormaps}, + {"Enable Internal Hi-Res", S_YESNO, m_null, G_X,G_Y+ 9*8, {"gl_texture_internal_hires"}, 0, 0, M_ChangeTextureUseHires}, + {"Enable External Hi-Res", S_YESNO, m_null, G_X,G_Y+10*8, {"gl_texture_external_hires"}, 0, 0, M_ChangeTextureUseHires}, + {"Override PWAD's graphics with Hi-Res" ,S_YESNO|S_PRGWARN,m_null,G_X,G_Y+11*8, {"gl_hires_override_pwads"}, 0, 0, M_ChangeTextureUseHires}, + + {"Enable High Quality Resize", S_YESNO, m_null, G_X, G_Y+13*8, {"gl_texture_hqresize"}, 0, 0, M_ChangeTextureHQResize}, + {"Resize textures", S_CHOICE, m_null, G_X, G_Y+14*8, {"gl_texture_hqresize_textures"}, 0, 0, M_ChangeTextureHQResize, gl_hqresizemodes}, + {"Resize sprites", S_CHOICE, m_null, G_X, G_Y+15*8, {"gl_texture_hqresize_sprites"}, 0, 0, M_ChangeTextureHQResize, gl_hqresizemodes}, + {"Resize patches", S_CHOICE, m_null, G_X, G_Y+16*8, {"gl_texture_hqresize_patches"}, 0, 0, M_ChangeTextureHQResize, gl_hqresizemodes}, + + {"Allow Detail Textures", S_YESNO, m_null, G_X, G_Y+18*8, {"gl_allow_detail_textures"}, 0, 0, M_ChangeUseDetail}, + {"Blend Animations", S_YESNO, m_null, G_X, G_Y+19*8, {"gl_blend_animations"}}, +#endif //GL_DOOM + + {"<- PREV",S_SKIP|S_PREV,m_null,KB_PREV,KB_Y+20*8, {gen_settings7}}, + {0,S_SKIP|S_END,m_null} +}; + +void M_Trans(void) // To reset translucency after setting it in menu +{ + general_translucency = default_translucency; //e6y: Fix for "translucency won't change until you restart the engine" + + if (general_translucency) + R_InitTranMap(0); +} + +// To (un)set fullscreen video after menu changes +void M_ChangeFullScreen(void) +{ + V_ToggleFullscreen(); +} + +void M_ChangeVideoMode(void) +{ + V_ChangeScreenResolution(); +} + +void M_ChangeUseGLSurface(void) +{ + V_ChangeScreenResolution(); +} + +void M_ChangeDemoSmoothTurns(void) +{ + R_SmoothPlaying_Reset(NULL); +} + +void M_ChangeTextureParams(void) +{ +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + gld_InitTextureParams(); + gld_FlushTextures(); + } +#endif +} + +// Setting up for the General screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_General(int choice) +{ + M_SetupNextMenu(&GeneralDef); + + setup_active = true; + setup_screen = ss_gen; + set_general_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = gen_settings[0]; + set_menu_itemon = M_GetSetupMenuItemOn(); + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// The drawing part of the General Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawGeneral(void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + // proff/nicolas 09/20/98 -- changed for hi-res + M_DrawTitle(114, 2, "M_GENERL", CR_DEFAULT, "GENERAL", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + +///////////////////////////// +// +// The Compatibility table. +// killough 10/10/98 + +#define C_X 284 +#define C_Y 32 +#define COMP_SPC 12 +#define C_NEXTPREV 131 + +setup_menu_t comp_settings1[], comp_settings2[], comp_settings3[]; +setup_menu_t comp_settings3[];//e6y + +setup_menu_t* comp_settings[] = +{ + comp_settings1, + comp_settings2, + comp_settings3, + NULL +}; + +enum +{ + compat_telefrag, + compat_dropoff, + compat_falloff, + compat_staylift, + compat_doorstuck, + compat_pursuit, + compat_vile, + compat_pain, + compat_skull, + compat_blazing, + compat_doorlight = 0, + compat_god, + compat_infcheat, + compat_zombie, + compat_skymap, + compat_stairs, + compat_floors, + compat_moveblock, + compat_model, + compat_zerotags, + compat_666 = 0, + compat_soul, + compat_maskedanim, + compat_sound, + //e6y + compat_plussettings, + compat_ouchface, + compat_maxhealth, + compat_translucency, + compat_skytransfers, +}; + +setup_menu_t comp_settings1[] = // Compatibility Settings screen #1 +{ + {"Any monster can telefrag on MAP30", S_YESNO, m_null, C_X, + C_Y + compat_telefrag * COMP_SPC, {"comp_telefrag"}}, + + {"Some objects never hang over tall ledges", S_YESNO, m_null, C_X, + C_Y + compat_dropoff * COMP_SPC, {"comp_dropoff"}}, + + {"Objects don't fall under their own weight", S_YESNO, m_null, C_X, + C_Y + compat_falloff * COMP_SPC, {"comp_falloff"}}, + + {"Monsters randomly walk off of moving lifts", S_YESNO, m_null, C_X, + C_Y + compat_staylift * COMP_SPC, {"comp_staylift"}}, + + {"Monsters get stuck on doortracks", S_YESNO, m_null, C_X, + C_Y + compat_doorstuck * COMP_SPC, {"comp_doorstuck"}}, + + {"Monsters don't give up pursuit of targets", S_YESNO, m_null, C_X, + C_Y + compat_pursuit * COMP_SPC, {"comp_pursuit"}}, + + {"Arch-Vile resurrects invincible ghosts", S_YESNO, m_null, C_X, + C_Y + compat_vile * COMP_SPC, {"comp_vile"}}, + + {"Pain Elementals limited to 21 lost souls", S_YESNO, m_null, C_X, + C_Y + compat_pain * COMP_SPC, {"comp_pain"}}, + + {"Lost souls get stuck behind walls", S_YESNO, m_null, C_X, + C_Y + compat_skull * COMP_SPC, {"comp_skull"}}, + + {"Blazing doors make double closing sounds", S_YESNO, m_null, C_X, + C_Y + compat_blazing * COMP_SPC, {"comp_blazing"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + {"NEXT ->",S_SKIP|S_NEXT, m_null, KB_NEXT, C_Y+C_NEXTPREV, {comp_settings2}}, + + // Final entry + {0,S_SKIP|S_END,m_null} +}; + +setup_menu_t comp_settings2[] = // Compatibility Settings screen #2 +{ + {"Tagged doors don't trigger special lighting", S_YESNO, m_null, C_X, + C_Y + compat_doorlight * COMP_SPC, {"comp_doorlight"}}, + + {"God mode isn't absolute", S_YESNO, m_null, C_X, + C_Y + compat_god * COMP_SPC, {"comp_god"}}, + + {"Powerup cheats are not infinite duration", S_YESNO, m_null, C_X, + C_Y + compat_infcheat * COMP_SPC, {"comp_infcheat"}}, + + {"Dead players can exit levels", S_YESNO, m_null, C_X, + C_Y + compat_zombie * COMP_SPC, {"comp_zombie"}}, + + {"Sky is unaffected by invulnerability", S_YESNO, m_null, C_X, + C_Y + compat_skymap * COMP_SPC, {"comp_skymap"}}, + + {"Use exactly Doom's stairbuilding method", S_YESNO, m_null, C_X, + C_Y + compat_stairs * COMP_SPC, {"comp_stairs"}}, + + {"Use exactly Doom's floor motion behavior", S_YESNO, m_null, C_X, + C_Y + compat_floors * COMP_SPC, {"comp_floors"}}, + + {"Use exactly Doom's movement clipping code", S_YESNO, m_null, C_X, + C_Y + compat_moveblock * COMP_SPC, {"comp_moveblock"}}, + + {"Use exactly Doom's linedef trigger model", S_YESNO, m_null, C_X, + C_Y + compat_model * COMP_SPC, {"comp_model"}}, + + {"Linedef effects work with sector tag = 0", S_YESNO, m_null, C_X, + C_Y + compat_zerotags * COMP_SPC, {"comp_zerotags"}}, + + {"NEXT ->",S_SKIP|S_NEXT, m_null, KB_NEXT, C_Y+C_NEXTPREV, {comp_settings3}},//e6y + {"<- PREV", S_SKIP|S_PREV, m_null, KB_PREV, C_Y+C_NEXTPREV,{comp_settings1}}, + + {"NEXT ->",S_SKIP|S_NEXT, m_null, KB_NEXT, C_Y+C_NEXTPREV, {comp_settings3}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +//e6y +setup_menu_t comp_settings3[] = // Compatibility Settings screen #3 +{ + {"Emulate pre-Ultimate BossDeath behaviour", S_YESNO, m_null, C_X, + C_Y + compat_666 * COMP_SPC, {"comp_666"}}, + + {"Lost souls don't bounce off flat surfaces", S_YESNO, m_null, C_X, + C_Y + compat_soul * COMP_SPC, {"comp_soul"}}, + + {"2S middle textures do not animate", S_YESNO, m_null, C_X, + C_Y + compat_maskedanim * COMP_SPC, {"comp_maskedanim"}}, + + //e6y + {"Retain quirks in Doom's sound code", S_YESNO, m_null, C_X, + C_Y + compat_sound * COMP_SPC, {"comp_sound"}}, + {"PrBoom-Plus Settings", S_SKIP|S_TITLE,m_null,C_X, + C_Y + compat_plussettings * COMP_SPC}, + {"Use Doom's buggy \"Ouch\" face code", S_YESNO, m_null, C_X, + C_Y + compat_ouchface * COMP_SPC, {"comp_ouchface"}}, + {"Max Health in DEH applies only to potions", S_YESNO, m_null, C_X, + C_Y + compat_maxhealth * COMP_SPC, {"comp_maxhealth"}}, + {"No predefined translucency for some things", S_YESNO, m_null, C_X, + C_Y + compat_translucency * COMP_SPC, {"comp_translucency"},0,0,deh_changeCompTranslucency}, + // [FG] + {"allow MBF sky transfers in all complevels", S_YESNO, m_null, C_X, + C_Y + compat_skytransfers * COMP_SPC, {"comp_skytransfers"}}, + {"<- PREV", S_SKIP|S_PREV, m_null, KB_PREV, C_Y+C_NEXTPREV,{comp_settings2}}, + {0,S_SKIP|S_END,m_null} +}; + +// Setting up for the Compatibility screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_Compat(int choice) +{ + M_SetupNextMenu(&CompatDef); + + setup_active = true; + setup_screen = ss_comp; + set_general_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = comp_settings[0]; + set_menu_itemon = M_GetSetupMenuItemOn(); + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// The drawing part of the Compatibility Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawCompat(void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + M_DrawTitle(52, 2, "M_COMPAT", CR_DEFAULT, "DOOM COMPATIBILITY", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + +///////////////////////////// +// +// The Messages table. + +#define M_X 230 +#define M_Y 39 + +// killough 11/98: enumerated + +enum { + mess_color_play, + mess_timer, + mess_color_chat, + mess_chat_timer, + mess_color_review, + mess_timed, + mess_hud_timer, + mess_lines, + mess_scrollup, + mess_background, +}; + +setup_menu_t mess_settings1[]; + +setup_menu_t* mess_settings[] = +{ + mess_settings1, + NULL +}; + +setup_menu_t mess_settings1[] = // Messages screen +{ + {"Message Color During Play", S_CRITEM, m_null, M_X, + M_Y + mess_color_play*8, {"hudcolor_mesg"}}, + +#if 0 + {"Message Duration During Play (ms)", S_NUM, m_null, M_X, + M_Y + mess_timer*8, {"message_timer"}}, +#endif + + {"Chat Message Color", S_CRITEM, m_null, M_X, + M_Y + mess_color_chat*8, {"hudcolor_chat"}}, + +#if 0 + {"Chat Message Duration (ms)", S_NUM, m_null, M_X, + M_Y + mess_chat_timer*8, {"chat_msg_timer"}}, +#endif + + {"Message Review Color", S_CRITEM, m_null, M_X, + M_Y + mess_color_review*8, {"hudcolor_list"}}, + +#if 0 + {"Message Listing Review is Temporary", S_YESNO, m_null, M_X, + M_Y + mess_timed*8, {"hud_msg_timed"}}, + + {"Message Review Duration (ms)", S_NUM, m_null, M_X, + M_Y + mess_hud_timer*8, {"hud_msg_timer"}}, +#endif + + {"Number of Review Message Lines", S_NUM, m_null, M_X, + M_Y + mess_lines*8, {"hud_msg_lines"}}, + +#if 0 + {"Message Listing Scrolls Upwards", S_YESNO, m_null, M_X, + M_Y + mess_scrollup*8, {"hud_msg_scrollup"}}, +#endif + + {"Message Background", S_YESNO, m_null, M_X, + M_Y + mess_background*8, {"hud_list_bgon"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + + +// Setting up for the Messages screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_Messages(int choice) +{ + M_SetupNextMenu(&MessageDef); + + setup_active = true; + setup_screen = ss_mess; + set_mess_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = mess_settings[0]; + set_menu_itemon = M_GetSetupMenuItemOn(); + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + + +// The drawing part of the Messages Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawMessages(void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + // CPhipps - patch drawing updated + M_DrawTitle(103, 2, "M_MESS", CR_DEFAULT, "MESSAGES", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + if (default_verify) + M_DrawDefVerify(); +} + + +///////////////////////////// +// +// The Chat Strings table. + +#define CS_X 20 +#define CS_Y (31+8) + +setup_menu_t chat_settings1[]; + +setup_menu_t* chat_settings[] = +{ + chat_settings1, + NULL +}; + +setup_menu_t chat_settings1[] = // Chat Strings screen +{ + {"1",S_CHAT,m_null,CS_X,CS_Y+ 1*8, {"chatmacro1"}}, + {"2",S_CHAT,m_null,CS_X,CS_Y+ 2*8, {"chatmacro2"}}, + {"3",S_CHAT,m_null,CS_X,CS_Y+ 3*8, {"chatmacro3"}}, + {"4",S_CHAT,m_null,CS_X,CS_Y+ 4*8, {"chatmacro4"}}, + {"5",S_CHAT,m_null,CS_X,CS_Y+ 5*8, {"chatmacro5"}}, + {"6",S_CHAT,m_null,CS_X,CS_Y+ 6*8, {"chatmacro6"}}, + {"7",S_CHAT,m_null,CS_X,CS_Y+ 7*8, {"chatmacro7"}}, + {"8",S_CHAT,m_null,CS_X,CS_Y+ 8*8, {"chatmacro8"}}, + {"9",S_CHAT,m_null,CS_X,CS_Y+ 9*8, {"chatmacro9"}}, + {"0",S_CHAT,m_null,CS_X,CS_Y+10*8, {"chatmacro0"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + // Final entry + {0,S_SKIP|S_END,m_null} + +}; + +// Setting up for the Chat Strings screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_ChatStrings(int choice) +{ + M_SetupNextMenu(&ChatStrDef); + setup_active = true; + setup_screen = ss_chat; + set_chat_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = chat_settings[0]; + set_menu_itemon = M_GetSetupMenuItemOn(); + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// The drawing part of the Chat Strings Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawChatStrings(void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + // CPhipps - patch drawing updated + M_DrawTitle(83, 2, "M_CHAT", CR_DEFAULT, "CHAT STRINGS", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + +///////////////////////////// +// +// General routines used by the Setup screens. +// + +static dboolean shiftdown = false; // phares 4/10/98: SHIFT key down or not + +// phares 4/17/98: +// M_SelectDone() gets called when you have finished entering your +// Setup Menu item change. + +static void M_SelectDone(setup_menu_t* ptr) +{ + ptr->m_flags &= ~S_SELECT; + ptr->m_flags |= S_HILITE; + S_StartSound(NULL,sfx_itemup); + setup_select = false; + colorbox_active = false; + if (print_warning_about_changes) // killough 8/15/98 + print_warning_about_changes--; +} + +// phares 4/21/98: +// Array of setup screens used by M_ResetDefaults() + +static setup_menu_t **setup_screens[] = +{ + keys_settings, + weap_settings, + stat_settings, + auto_settings, + enem_settings, + mess_settings, + chat_settings, + gen_settings, // killough 10/98 + comp_settings, +}; + +// phares 4/19/98: +// M_ResetDefaults() resets all values for a setup screen to default values +// +// killough 10/98: rewritten to fix bugs and warn about pending changes + +static void M_ResetDefaults(void) +{ + int i; //e6y + + default_t *dp; + int warn = 0; + + // Look through the defaults table and reset every variable that + // belongs to the group we're interested in. + // + // killough: However, only reset variables whose field in the + // current setup screen is the same as in the defaults table. + // i.e. only reset variables really in the current setup screen. + + // e6y + // Fixed crash while trying to read data past array end + // All previous versions of prboom worked only by a lucky accident + // old code: for (dp = defaults; dp->name; dp++) + for (i = 0; i < numdefaults ; i++) + { + dp = &defaults[i]; + + if (dp->setupscreen == setup_screen) + { + setup_menu_t **l, *p; + for (l = setup_screens[setup_screen-1]; *l; l++) + for (p = *l; !(p->m_flags & S_END); p++) + if (p->m_flags & S_HASDEFPTR ? p->var.def == dp : + p->var.m_key == dp->location.pi || + p->m_mouse == dp->location.pi || + p->m_joy == dp->location.pi) + { + if (IS_STRING(*dp)) + { + union { const char **c; char **s; } u; // type punning via unions + + u.c = dp->location.ppsz; + free(*(u.s)); + *(u.c) = strdup(dp->defaultvalue.psz); + } + else + *dp->location.pi = dp->defaultvalue.i; + +#if 0 + if (p->m_flags & (S_LEVWARN | S_PRGWARN)) + warn |= p->m_flags & (S_LEVWARN | S_PRGWARN); + else + if (dp->current) + if (allow_changes()) + *dp->current = *dp->location.pi; + else + warn |= S_LEVWARN; +#endif + if (p->action) + p->action(); + + goto end; + } + end:; + } + } + + if (warn) + warn_about_changes(warn); +} + +// +// M_InitDefaults() +// +// killough 11/98: +// +// This function converts all setup menu entries consisting of cfg +// variable names, into pointers to the corresponding default[] +// array entry. var.name becomes converted to var.def. +// + +static void M_InitDefaults(void) +{ + setup_menu_t *const *p, *t; + default_t *dp; + int i; + for (i = 0; i < ss_max-1; i++) + { + for (p = setup_screens[i]; *p; p++) + { + for (t = *p; !(t->m_flags & S_END); t++) + { + if (t->m_flags & S_HASDEFPTR) + { + if (!(dp = M_LookupDefault(t->var.name))) + I_Error("M_InitDefaults: Couldn't find config variable %s", t->var.name); + else + (t->var.def = dp)->setup_menu = t; + } + } + } + } +} + +// +// End of Setup Screens. +// +///////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// +// Start of Extended HELP screens // phares 3/30/98 +// +// The wad designer can define a set of extended HELP screens for their own +// information display. These screens should be 320x200 graphic lumps +// defined in a separate wad. They should be named "HELP01" through "HELP99". +// "HELP01" is shown after the regular BOOM Dynamic HELP screen, and ENTER +// and BACKSPACE keys move the player through the HELP set. +// +// Rather than define a set of menu definitions for each of the possible +// HELP screens, one definition is used, and is altered on the fly +// depending on what HELPnn lumps the game finds. + +// phares 3/30/98: +// Extended Help Screen variables + +int extended_help_count; // number of user-defined help screens found +int extended_help_index; // index of current extended help screen + +menuitem_t ExtHelpMenu[] = +{ + {1,"",M_ExtHelpNextScreen,0} +}; + +menu_t ExtHelpDef = +{ + 1, // # of menu items + &ReadDef1, // previous menu + ExtHelpMenu, // menuitem_t -> + M_DrawExtHelp, // drawing routine -> + 330,181, // x,y + 0 // lastOn +}; + +// M_ExtHelpNextScreen establishes the number of the next HELP screen in +// the series. + +void M_ExtHelpNextScreen(int choice) +{ + choice = 0; + if (++extended_help_index > extended_help_count) + { + + // when finished with extended help screens, return to Main Menu + + extended_help_index = 1; + M_SetupNextMenu(&MainDef); + } +} + +// phares 3/30/98: +// Routine to look for HELPnn screens and create a menu +// definition structure that defines extended help screens. + +void M_InitExtendedHelp(void) + +{ + int index,i; + char namebfr[] = { "HELPnn"} ; + + extended_help_count = 0; + for (index = 1 ; index < 100 ; index++) { + namebfr[4] = index/10 + 0x30; + namebfr[5] = index%10 + 0x30; + i = W_CheckNumForName(namebfr); + if (i == -1) { + if (extended_help_count) { + /* The Extended Help menu is accessed using the + * Help hotkey (F1) or the "Read This!" menu item. + * + * If Extended Help screens are present, use the + * Extended Help routine when either the F1 Help Menu + * or the "Read This!" menu items are accessed. + * + * See also: https://www.doomworld.com/forum/topic/111465-boom-extended-help-screens-an-undocumented-feature/ + */ + HelpMenu[0].routine = M_ExtHelp; + if (gamemode == commercial) { + ExtHelpDef.prevMenu = &ReadDef1; /* previous menu */ + ReadMenu1[0].routine = M_ExtHelp; + } else { + ExtHelpDef.prevMenu = &ReadDef2; /* previous menu */ + ReadMenu2[0].routine = M_ExtHelp; + } + } + return; + } + extended_help_count++; + } + +} + +// Initialization for the extended HELP screens. + +void M_ExtHelp(int choice) +{ + choice = 0; + extended_help_index = 1; // Start with first extended help screen + M_SetupNextMenu(&ExtHelpDef); +} + +// Initialize the drawing part of the extended HELP screens. + +void M_DrawExtHelp(void) +{ + char namebfr[10] = { "HELPnn" }; // CPhipps - make it local & writable + + inhelpscreens = true; // killough 5/1/98 + namebfr[4] = extended_help_index/10 + 0x30; + namebfr[5] = extended_help_index%10 + 0x30; + // CPhipps - patch drawing updated + V_DrawNamePatch(0, 0, 0, namebfr, CR_DEFAULT, VPT_STRETCH); +} + +// +// End of Extended HELP screens // phares 3/30/98 +// +//////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////// +// +// Dynamic HELP screen // phares 3/2/98 +// +// Rather than providing the static HELP screens from DOOM and its versions, +// BOOM provides the player with a dynamic HELP screen that displays the +// current settings of major key bindings. +// +// The Dynamic HELP screen is defined in a manner similar to that used for +// the Setup Screens above. +// +// M_GetKeyString finds the correct string to represent the key binding +// for the current item being drawn. + +int M_GetKeyString(int c,int offset) +{ + const char* s; + + if (c >= 33 && c <= 126) { + + // The '=', ',', and '.' keys originally meant the shifted + // versions of those keys, but w/o having to shift them in + // the game. Any actions that are mapped to these keys will + // still mean their shifted versions. Could be changed later + // if someone can come up with a better way to deal with them. + + if (c == '=') // probably means the '+' key? + c = '+'; + else if (c == ',') // probably means the '<' key? + c = '<'; + else if (c == '.') // probably means the '>' key? + c = '>'; + menu_buffer[offset++] = c; // Just insert the ascii key + menu_buffer[offset] = 0; + + } else { + + // Retrieve 4-letter (max) string representing the key + + // cph - Keypad keys, general code reorganisation to + // make this smaller and neater. + if ((0x100 <= c) && (c < 0x200)) { + if (c == KEYD_KEYPADENTER) + s = "PADE"; + else { + strcpy(&menu_buffer[offset], "PAD"); + offset+=4; + menu_buffer[offset-1] = c & 0xff; + menu_buffer[offset] = 0; + } + } else if ((KEYD_F1 <= c) && (c < KEYD_F10)) { + menu_buffer[offset++] = 'F'; + menu_buffer[offset++] = '1' + c - KEYD_F1; + menu_buffer[offset] = 0; + } else { + switch(c) { + case KEYD_TAB: s = "TAB"; break; + case KEYD_ENTER: s = "ENTR"; break; + case KEYD_ESCAPE: s = "ESC"; break; + case KEYD_SPACEBAR: s = "SPAC"; break; + case KEYD_BACKSPACE: s = "BACK"; break; + case KEYD_RCTRL: s = "CTRL"; break; + case KEYD_LEFTARROW: s = "LARR"; break; + case KEYD_UPARROW: s = "UARR"; break; + case KEYD_RIGHTARROW: s = "RARR"; break; + case KEYD_DOWNARROW: s = "DARR"; break; + case KEYD_RSHIFT: s = "SHFT"; break; + case KEYD_RALT: s = "ALT"; break; + case KEYD_CAPSLOCK: s = "CAPS"; break; + case KEYD_SCROLLLOCK: s = "SCRL"; break; + case KEYD_HOME: s = "HOME"; break; + case KEYD_PAGEUP: s = "PGUP"; break; + case KEYD_END: s = "END"; break; + case KEYD_PAGEDOWN: s = "PGDN"; break; + case KEYD_INSERT: s = "INST"; break; + case KEYD_DEL: s = "DEL"; break; + case KEYD_F10: s = "F10"; break; + case KEYD_F11: s = "F11"; break; + case KEYD_F12: s = "F12"; break; + case KEYD_PAUSE: s = "PAUS"; break; + case KEYD_MWHEELDOWN: s = "MWDN"; break; + case KEYD_MWHEELUP: s = "MWUP"; break; + case KEYD_PRINTSC: s = "PRSC"; break; + case 0: s = "NONE"; break; + default: s = "JUNK"; break; + } + + if (s) { // cph - Slight code change + strcpy(&menu_buffer[offset],s); // string to display + offset += strlen(s); + } + } + } + return offset; +} + +// +// The Dynamic HELP screen table. + +#define KT_X1 283 +#define KT_X2 172 +#define KT_X3 87 + +#define KT_Y1 2 +#define KT_Y2 118 +#define KT_Y3 102 + +setup_menu_t helpstrings[] = // HELP screen strings +{ + {"SCREEN" ,S_SKIP|S_TITLE,m_null,KT_X1,KT_Y1}, + {"HELP" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 1*8,{&key_help}}, + {"MENU" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 2*8,{&key_escape}}, + {"SETUP" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 3*8,{&key_setup}}, + {"PAUSE" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 4*8,{&key_pause}}, + {"AUTOMAP" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 5*8,{&key_map}}, + {"SOUND VOLUME",S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 6*8,{&key_soundvolume}}, + {"HUD" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 7*8,{&key_hud}}, + {"MESSAGES" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 8*8,{&key_messages}}, + {"GAMMA FIX" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 9*8,{&key_gamma}}, + {"SPY" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+10*8,{&key_spy}}, + {"LARGER VIEW" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+11*8,{&key_zoomin}}, + {"SMALLER VIEW",S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+12*8,{&key_zoomout}}, + {"SCREENSHOT" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+13*8,{&key_screenshot}}, + + {"AUTOMAP" ,S_SKIP|S_TITLE,m_null,KT_X1,KT_Y2}, + {"FOLLOW MODE" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 1*8,{&key_map_follow}}, + {"ZOOM IN" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 2*8,{&key_map_zoomin}}, + {"ZOOM OUT" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 3*8,{&key_map_zoomout}}, + {"MARK PLACE" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 4*8,{&key_map_mark}}, + {"CLEAR MARKS" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 5*8,{&key_map_clear}}, + {"FULL/ZOOM" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 6*8,{&key_map_gobig}}, + {"GRID" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 7*8,{&key_map_grid}}, + {"ROTATE" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 8*8,{&key_map_rotate}}, + {"OVERLAY" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 9*8,{&key_map_overlay}}, + + {"WEAPONS" ,S_SKIP|S_TITLE,m_null,KT_X3,KT_Y1}, + {"FIST" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 1*8,{&key_weapon1},&mb_weapon1}, + {"PISTOL" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 2*8,{&key_weapon2},&mb_weapon2}, + {"SHOTGUN" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 3*8,{&key_weapon3},&mb_weapon3}, + {"CHAINGUN" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 4*8,{&key_weapon4},&mb_weapon4}, + {"ROCKET" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 5*8,{&key_weapon5},&mb_weapon5}, + {"PLASMA" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 6*8,{&key_weapon6},&mb_weapon6}, + {"BFG 9000" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 7*8,{&key_weapon7},&mb_weapon7}, + {"CHAINSAW" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 8*8,{&key_weapon8},&mb_weapon8}, + {"SSG" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 9*8,{&key_weapon9},&mb_weapon9}, + {"BEST" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+10*8,{&key_weapontoggle}}, + {"FIRE" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+11*8,{&key_fire},&mousebfire,&joybfire}, + + {"MOVEMENT" ,S_SKIP|S_TITLE,m_null,KT_X3,KT_Y3}, + {"FORWARD" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 1*8,{&key_up},&mousebforward}, + {"BACKWARD" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 2*8,{&key_down},&mousebbackward}, + {"TURN LEFT" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 3*8,{&key_left},&mousebturnleft}, + {"TURN RIGHT" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 4*8,{&key_right},&mousebturnright}, + {"RUN" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 5*8,{&key_speed},&mousebspeed,&joybspeed}, + {"STRAFE LEFT" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 6*8,{&key_strafeleft},0,&joybstrafeleft}, + {"STRAFE RIGHT",S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 7*8,{&key_straferight},0,&joybstraferight}, + {"STRAFE" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 8*8,{&key_strafe},&mousebstrafe,&joybstrafe}, + {"AUTORUN" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 9*8,{&key_autorun}}, + {"180 TURN" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+10*8,{&key_reverse}}, + {"USE" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+11*8,{&key_use},&mousebuse,&joybuse}, + + {"GAME" ,S_SKIP|S_TITLE,m_null,KT_X2,KT_Y1}, + {"SAVE" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 1*8,{&key_savegame}}, + {"LOAD" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 2*8,{&key_loadgame}}, + {"QUICKSAVE" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 3*8,{&key_quicksave}}, + {"END GAME" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 4*8,{&key_endgame}}, + {"QUICKLOAD" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 5*8,{&key_quickload}}, + {"QUIT" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 6*8,{&key_quit}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +#define SPACEWIDTH 4 + +/* cph 2006/08/06 + * M_DrawString() is the old M_DrawMenuString, except that it is not tied to + * menu_buffer - no reason to force all the callers to write into one array! */ + +static void M_DrawString(int cx, int cy, int color, const char* ch) +{ + int w; + int c; + + while (*ch) { + c = *ch++; // get next char + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) + { + cx += SPACEWIDTH; // space + continue; + } + w = hu_font[c].width; + if (cx + w > 320) + break; + + // V_DrawpatchTranslated() will draw the string in the + // desired color, colrngs[color] + + // CPhipps - patch drawing updated + V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, color, VPT_STRETCH | VPT_TRANS); + // The screen is cramped, so trim one unit from each + // character so they butt up against each other. + cx += w - 1; + } +} + +// M_DrawMenuString() draws the string in menu_buffer[] + +static void M_DrawMenuString(int cx, int cy, int color) +{ + M_DrawString(cx, cy, color, menu_buffer); +} + +// M_GetPixelWidth() returns the number of pixels in the width of +// the string, NOT the number of chars in the string. + +static int M_GetPixelWidth(const char* ch) +{ + int len = 0; + int c; + + while (*ch) { + c = *ch++; // pick up next char + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c > HU_FONTSIZE) + { + len += SPACEWIDTH; // space + continue; + } + len += hu_font[c].width; + len--; // adjust so everything fits + } + len++; // replace what you took away on the last char only + return len; +} + +static void M_DrawStringCentered(int cx, int cy, int color, const char* ch) +{ + M_DrawString(cx - M_GetPixelWidth(ch)/2, cy, color, ch); +} + +// +// M_DrawHelp +// +// This displays the help screen + +void M_DrawHelp (void) +{ + const int helplump = W_CheckNumForName("HELP"); + + menuactive = mnact_full; + + if (helplump >= 0 && lumpinfo[helplump].source != source_iwad) + { + V_FillBorder(-1, 0); + V_DrawNumPatch(0, 0, 0, helplump, CR_DEFAULT, VPT_STRETCH); + } + else + { + M_DrawBackground("FLOOR4_6", 0); + M_DrawScreenItems(helpstrings); + } +} + +// +// End of Dynamic HELP screen // phares 3/2/98 +// +//////////////////////////////////////////////////////////////////////////// + +enum { + prog, + prog_stub, + prog_stub1, + prog_stub2, + adcr +}; + +enum { + cr_prog=0, + cr_adcr=2, +}; + +#define CR_S 9 +#define CR_X 20 +#define CR_X2 50 +#define CR_Y 32 +#define CR_SH 9 + +setup_menu_t cred_settings[]={ + + {"Programmers",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X, CR_Y + CR_S*prog + CR_SH*cr_prog}, + {"Florian 'Proff' Schulze",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(prog+1) + CR_SH*cr_prog}, + {"Colin Phipps",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(prog+2) + CR_SH*cr_prog}, + {"Neil Stevens",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(prog+3) + CR_SH*cr_prog}, + {"Andrey Budko",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(prog+4) + CR_SH*cr_prog}, + + {"Additional Credit To",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X, CR_Y + CR_S*adcr + CR_SH*cr_adcr}, + {"id Software for DOOM",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+1)+CR_SH*cr_adcr}, + {"TeamTNT for BOOM",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+2)+CR_SH*cr_adcr}, + {"Lee Killough for MBF",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+3)+CR_SH*cr_adcr}, + {"The DOSDoom-Team for DOSDOOM",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+4)+CR_SH*cr_adcr}, + {"Randy Heit for ZDOOM",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+5)+CR_SH*cr_adcr}, + {"Michael 'Kodak' Ryssen for DOOMGL",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+6)+CR_SH*cr_adcr}, + {"Jess Haas for lSDLDoom",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+7) + CR_SH*cr_adcr}, + {"all others who helped (see AUTHORS file)",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+8)+CR_SH*cr_adcr}, + + {0,S_SKIP|S_END,m_null} +}; + +void M_DrawCredits(void) // killough 10/98: credit screen +{ + const int creditlump = W_CheckNumForName("CREDIT"); + + inhelpscreens = true; + if (creditlump >= 0 && lumpinfo[creditlump].source != source_iwad) + { + V_FillBorder(-1, 0); + V_DrawNumPatch(0, 0, 0, creditlump, CR_DEFAULT, VPT_STRETCH); + } + else + { + // Use V_DrawBackground here deliberately to force drawing a background + V_DrawBackground(gamemode==shareware ? "CEIL5_1" : "MFLR8_4", 0); + M_DrawTitle(81, 9, "PRBOOM", CR_GOLD, PACKAGE_NAME " v" PACKAGE_VERSION, CR_GOLD); + M_DrawScreenItems(cred_settings); + } +} + +static int M_IndexInChoices(const char *str, const char **choices) { + int i = 0; + + while (*choices != NULL) { + if (!strcmp(str, *choices)) + return i; + i++; + choices++; + } + return 0; +} + +// [FG] support more joystick and mouse buttons + +static inline int GetButtons(const unsigned int max, int data) +{ + int i; + + for (i = 0; i < max; ++i) + { + if (data & (1 << i)) + { + return i; + } + } + + return -1; +} + +///////////////////////////////////////////////////////////////////////////// +// +// M_Responder +// +// Examines incoming keystrokes and button pushes and determines some +// action based on the state of the system. +// + +dboolean M_Responder (event_t* ev) { + int ch; + int i; + static int joywait = 0; + static int mousewait = 0; + + ch = -1; // will be changed to a legit char if we're going to use it here + + // Process joystick input + + if (ev->type == ev_joystick && joywait < I_GetTime()) { + if (ev->data3 == -1) + { + ch = key_menu_up; // phares 3/7/98 + joywait = I_GetTime() + 5; + } + else if (ev->data3 == 1) + { + ch = key_menu_down; // phares 3/7/98 + joywait = I_GetTime() + 5; + } + + if (ev->data2 == -1) + { + ch = key_menu_left; // phares 3/7/98 + joywait = I_GetTime() + 2; + } + else if (ev->data2 == 1) + { + ch = key_menu_right; // phares 3/7/98 + joywait = I_GetTime() + 2; + } + + if (ev->data1&1) + { + ch = key_menu_enter; // phares 3/7/98 + joywait = I_GetTime() + 5; + } + + if (ev->data1&2) + { + ch = key_menu_backspace; // phares 3/7/98 + joywait = I_GetTime() + 5; + } + + // phares 4/4/98: + // Handle joystick buttons 3 and 4, and allow them to pass down + // to where key binding can eat them. + + if (setup_active && set_keybnd_active) { + if (ev->data1&4) { + ch = 0; // meaningless, just to get you past the check for -1 + joywait = I_GetTime() + 5; + } + if (ev->data1&8) { + ch = 0; // meaningless, just to get you past the check for -1 + joywait = I_GetTime() + 5; + } + } + + } else { + // Process mouse input + if (ev->type == ev_mouse && mousewait < I_GetTime()) { + + if (ev->data1&1) + { + ch = key_menu_enter; // phares 3/7/98 + mousewait = I_GetTime() + 15; + } + + if (ev->data1&2) + { + ch = key_menu_backspace; // phares 3/7/98 + mousewait = I_GetTime() + 15; + } + + // phares 4/4/98: + // Handle mouse button 3, and allow it to pass down + // to where key binding can eat it. + + if (setup_active && set_keybnd_active) + if (ev->data1 >> 2) + { + ch = 0; // meaningless, just to get you past the check for -1 + mousewait = I_GetTime() + 15; + } + } + else + + // Process keyboard input + + if (ev->type == ev_keydown) + { + ch = ev->data1; // phares 4/11/98: + if (ch == KEYD_RSHIFT) // For chat string processing, need + shiftdown = true; // to know when shift key is up or + } // down so you can get at the !,#, + else if (ev->type == ev_keyup) // etc. keys. Keydowns are allowed + if (ev->data1 == KEYD_RSHIFT) // past this point, but keyups aren't + shiftdown = false; // so we need to note the difference + } // here using the 'shiftdown' dboolean. + + if (ch == -1) + return false; // we can't use the event here + + // Save Game string input + + if (saveStringEnter) { + if (ch == key_menu_backspace) // phares 3/7/98 + { + if (saveCharIndex > 0) + { + saveCharIndex--; + savegamestrings[saveSlot][saveCharIndex] = 0; + } + } + + else if (ch == key_menu_escape) // phares 3/7/98 + { + saveStringEnter = 0; + strcpy(&savegamestrings[saveSlot][0],saveOldString); + } + + else if (ch == key_menu_enter) // phares 3/7/98 + { + saveStringEnter = 0; + if (savegamestrings[saveSlot][0]) + M_DoSave(saveSlot); + } + + else + { + ch = toupper(ch); + if (ch >= 32 && ch <= 127 && + saveCharIndex < SAVESTRINGSIZE-1 && + M_StringWidth(savegamestrings[saveSlot]) < (SAVESTRINGSIZE-2)*8) + { + savegamestrings[saveSlot][saveCharIndex++] = ch; + savegamestrings[saveSlot][saveCharIndex] = 0; + } + } + return true; + } + + // Take care of any messages that need input + + if (messageToPrint) { + if (messageNeedsInput == true && + !(ch == ' ' || ch == 'n' || ch == 'y' || ch == key_escape)) // phares + return false; + + menuactive = messageLastMenuActive; + messageToPrint = 0; + if (messageRoutine) + messageRoutine(ch); + + menuactive = mnact_inactive; + S_StartSound(NULL,sfx_swtchx); + return true; + } + + // killough 2/22/98: add support for screenshot key: + if (ch == key_screenshot) + { + G_ScreenShot (); + // Don't eat the keypress in this case. See sf bug #1843280. + } + + // If there is no active menu displayed... + + if (!menuactive) { // phares + if (ch == key_autorun) // Autorun // V + { + autorun = !autorun; + return true; + } + + if (ch == key_help) // Help key + { + M_StartControlPanel (); + + currentMenu = &HelpDef; // killough 10/98: new help screen + + itemOn = 0; + S_StartSound(NULL,sfx_swtchn); + return true; + } + + if (ch == key_savegame) // Save Game + { + M_StartControlPanel(); + S_StartSound(NULL,sfx_swtchn); + M_SaveGame(0); + return true; + } + + if (ch == key_loadgame) // Load Game + { + M_StartControlPanel(); + S_StartSound(NULL,sfx_swtchn); + M_LoadGame(0); + return true; + } + + if (ch == key_soundvolume) // Sound Volume + { + M_StartControlPanel (); + currentMenu = &SoundDef; + itemOn = sfx_vol; + S_StartSound(NULL,sfx_swtchn); + return true; + } + + if (ch == key_quicksave) // Quicksave + { + S_StartSound(NULL,sfx_swtchn); + M_QuickSave(); + return true; + } + + if (ch == key_endgame) // End game + { + S_StartSound(NULL,sfx_swtchn); + M_EndGame(0); + return true; + } + + if (ch == key_messages) // Toggle messages + { + M_ChangeMessages(0); + S_StartSound(NULL,sfx_swtchn); + return true; + } + + if (ch == key_quickload) // Quickload + { + S_StartSound(NULL,sfx_swtchn); + M_QuickLoad(); + return true; + } + + if (ch == key_quit) // Quit DOOM + { + S_StartSound(NULL,sfx_swtchn); + M_QuitDOOM(0); + return true; + } + + if (ch == key_gamma) // gamma toggle + { +//e6y +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL && gl_hardware_gamma) + { + static char str[200]; + useglgamma++; + if (useglgamma > MAX_GLGAMMA) + useglgamma = 0; + sprintf(str, "Gamma correction level %d", useglgamma); + players[consoleplayer].message = str; + + gld_SetGammaRamp(useglgamma); + } + else +#endif + { + usegamma++; + if (usegamma > 4) + usegamma = 0; + players[consoleplayer].message = + usegamma == 0 ? s_GAMMALVL0 : + usegamma == 1 ? s_GAMMALVL1 : + usegamma == 2 ? s_GAMMALVL2 : + usegamma == 3 ? s_GAMMALVL3 : + s_GAMMALVL4; + V_SetPalette(0); + return true; + } + } + + + if (ch == key_zoomout) // zoom out + { + if ((automapmode & am_active) || chat_on) + return false; + M_SizeDisplay(0); + S_StartSound(NULL,sfx_stnmov); + return true; + } + + if (ch == key_zoomin) // zoom in + { // jff 2/23/98 + if ((automapmode & am_active) || chat_on) // allow + return false; // key_hud==key_zoomin + M_SizeDisplay(1); // ^ + S_StartSound(NULL,sfx_stnmov); // | + return true; // phares + } + + //e6y + if (ch == key_speed_default && (!netgame||demoplayback)) + { + realtic_clock_rate = StepwiseSum(realtic_clock_rate, 0, speed_step, 3, 10000, 100); + I_Init2(); + // Don't eat the keypress in this case. + // return true; + } + if (ch == key_speed_up && (!netgame||demoplayback)) + { + realtic_clock_rate = StepwiseSum(realtic_clock_rate, 1, speed_step, 3, 10000, 100); + I_Init2(); + // Don't eat the keypress in this case. + // return true; + } + if (ch == key_speed_down && (!netgame||demoplayback)) + { + realtic_clock_rate = StepwiseSum(realtic_clock_rate, -1, speed_step, 3, 10000, 100); + I_Init2(); + // Don't eat the keypress in this case. + // return true; + } + if (ch == key_nextlevel) + { + if (demoplayback && !doSkip && singledemo) + { + demo_stoponnext = true; + G_SkipDemoStart(); + return true; + } + else + { + if (G_GotoNextLevel(NULL, NULL)) + return true; + } + } + + if (ch == key_level_restart) + { + if (G_ReloadLevel()) + return true; + } + + if (ch == key_demo_endlevel) + { + if (demoplayback && !doSkip && singledemo) + { + demo_stoponend = true; + G_SkipDemoStart(); + return true; + } + } + + if (ch == key_demo_skip) + { + if (demoplayback && singledemo) + { + if (doSkip) + { + G_SkipDemoStop(); + } + else + { + G_SkipDemoStart(); + } + return true; + } + } + + if (ch == key_walkcamera) + { + if (demoplayback && gamestate == GS_LEVEL) + { + walkcamera.type = (walkcamera.type+1)%3; + P_SyncWalkcam (true, (walkcamera.type!=2)); + R_ResetViewInterpolation (); + if (walkcamera.type==0) + R_SmoothPlaying_Reset(NULL); + // Don't eat the keypress in this case. + // return true; + } + } + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + if (ch == key_showalive) + { + show_alive = (show_alive + 1) % 3; + doom_printf("Show Alive Monsters %s", + (show_alive ? (show_alive == 1 ? "(mode 1) on" : "(mode 2) on" ) : "off")); + } + } +#endif + + if (ch == key_mlook) // mouse look + { + movement_mouselook = !movement_mouselook; + M_ChangeMouseLook(); + doom_printf("Mouselook %s", movement_mouselook ? "on" : "off"); + // Don't eat the keypress in this case. + // return true; + } + + if (ch == key_novert) + { + movement_mousenovert = !movement_mousenovert; + doom_printf("Vertical Mouse Movement %s", movement_mousenovert ? "off" : "on"); + // Don't eat the keypress in this case. + // return true; + } + + if (ch == key_hud) // heads-up mode + { + if ((automapmode & am_active) || chat_on) // jff 2/22/98 + return false; // HUD mode control + if (screenSize<8) // function on default F5 + while (screenSize<8 || !hud_displayed) // make hud visible + M_SizeDisplay(1); // when configuring it + else + { + hud_displayed = 1; //jff 3/3/98 turn hud on + HU_NextHud(); + HU_MoveHud(true); //jff 3/9/98 move it now to avoid glitch + } + return true; + } + + /* killough 10/98: allow key shortcut into Setup menu */ + if (ch == key_setup) { + M_StartControlPanel(); + S_StartSound(NULL,sfx_swtchn); + M_SetupNextMenu(&SetupDef); + return true; + } + } + // Pop-up Main menu? + + if (!menuactive) + { + if (ch == key_escape) // phares + { + M_StartControlPanel (); + S_StartSound(NULL,sfx_swtchn); + return true; + } + return false; + } + + // [FG] delete a savegame + + if (currentMenu == &LoadDef || currentMenu == &SaveDef) + { + if (delete_verify) + { + if (toupper(ch) == 'Y') + { + M_DeleteGame(itemOn); + S_StartSound(NULL,sfx_itemup); + delete_verify = false; + } + else if (toupper(ch) == 'N') + { + S_StartSound(NULL,sfx_itemup); + delete_verify = false; + } + return true; + } + } + + // phares 3/26/98 - 4/11/98: + // Setup screen key processing + + if (setup_active) { + setup_menu_t* ptr1= current_setup_menu + set_menu_itemon; + setup_menu_t* ptr2 = NULL; + + // phares 4/19/98: + // Catch the response to the 'reset to default?' verification + // screen + + if (default_verify) + { + if (toupper(ch) == 'Y') { + M_ResetDefaults(); + default_verify = false; + M_SelectDone(ptr1); + } + else if (toupper(ch) == 'N') { + default_verify = false; + M_SelectDone(ptr1); + } + return true; + } + + // Common processing for some items + + if (setup_select) { // changing an entry + if (ch == key_menu_escape) // Exit key = no change + { + M_SelectDone(ptr1); // phares 4/17/98 + setup_gather = false; // finished gathering keys, if any + return true; + } + + if (ptr1->m_flags & S_YESNO) // yes or no setting? + { + if (ch == key_menu_enter) { + *ptr1->var.def->location.pi = !*ptr1->var.def->location.pi; // killough 8/15/98 + + // phares 4/14/98: + // If not in demoplayback, demorecording, or netgame, + // and there's a second variable in var2, set that + // as well + + // killough 8/15/98: add warning messages + + if (ptr1->m_flags & (S_LEVWARN | S_PRGWARN)) + warn_about_changes(ptr1->m_flags & // killough 10/98 + (S_LEVWARN | S_PRGWARN)); + else + M_UpdateCurrent(ptr1->var.def); + + if (ptr1->action) // killough 10/98 + ptr1->action(); + + //e6y +#ifdef GL_DOOM + { + extern dboolean gl_arb_multitexture; + if ((ptr1->m_flags&S_CANT_GL_ARB_MULTITEXTURE) && !gl_arb_multitexture) + warn_about_changes(ptr1->m_flags & S_CANT_GL_ARB_MULTITEXTURE); + } +#endif + } + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + if (ptr1->m_flags & S_CRITEM) + { + if (ch != key_menu_enter) + { + ch -= 0x30; // out of ascii + if (ch < 0 || ch > 9) + return true; // ignore + *ptr1->var.def->location.pi = ch; + } + if (ptr1->action) // killough 10/98 + ptr1->action(); + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + if (ptr1->m_flags & S_NUM) // number? + { + if (setup_gather) { // gathering keys for a value? + /* killough 10/98: Allow negatives, and use a more + * friendly input method (e.g. don't clear value early, + * allow backspace, and return to original value if bad + * value is entered). + */ + if (ch == key_menu_enter) { + if (gather_count) { // Any input? + int value; + + gather_buffer[gather_count] = 0; + value = atoi(gather_buffer); // Integer value + + //e6y + if ((ptr1->m_flags&S_CANT_GL_ARB_MULTISAMPLEFACTOR) && value%2!=0) + warn_about_changes(ptr1->m_flags & S_CANT_GL_ARB_MULTISAMPLEFACTOR); + else + + if ((ptr1->var.def->minvalue != UL && + value < ptr1->var.def->minvalue) || + (ptr1->var.def->maxvalue != UL && + value > ptr1->var.def->maxvalue)) + warn_about_changes(S_BADVAL); + else { + *ptr1->var.def->location.pi = value; + + /* killough 8/9/98: fix numeric vars + * killough 8/15/98: add warning message + */ + if (ptr1->m_flags & (S_LEVWARN | S_PRGWARN)) + warn_about_changes(ptr1->m_flags & + (S_LEVWARN | S_PRGWARN)); + else + M_UpdateCurrent(ptr1->var.def); + + if (ptr1->action) // killough 10/98 + ptr1->action(); + } + } + M_SelectDone(ptr1); // phares 4/17/98 + setup_gather = false; // finished gathering keys + return true; + } + + if (ch == key_menu_backspace && gather_count) { + gather_count--; + return true; + } + + if (gather_count >= MAXGATHER) + return true; + + if (!isdigit(ch) && ch != '-') + return true; // ignore + + /* killough 10/98: character-based numerical input */ + gather_buffer[gather_count++] = ch; + } + return true; + } + + if (ptr1->m_flags & S_CHOICE) // selection of choices? + { + if (ch == key_menu_left) { + if (ptr1->var.def->type == def_int) { + int value = *ptr1->var.def->location.pi; + + value = value - 1; + if ((ptr1->var.def->minvalue != UL && + value < ptr1->var.def->minvalue)) + value = ptr1->var.def->minvalue; + if ((ptr1->var.def->maxvalue != UL && + value > ptr1->var.def->maxvalue)) + value = ptr1->var.def->maxvalue; + if (*ptr1->var.def->location.pi != value) + S_StartSound(NULL,sfx_pstop); + *ptr1->var.def->location.pi = value; + } + if (ptr1->var.def->type == def_str) { + int old_value, value; + + old_value = M_IndexInChoices(*ptr1->var.def->location.ppsz, + ptr1->selectstrings); + value = old_value - 1; + if (value < 0) + value = 0; + if (old_value != value) + S_StartSound(NULL,sfx_pstop); + *ptr1->var.def->location.ppsz = ptr1->selectstrings[value]; + } + } + if (ch == key_menu_right) { + if (ptr1->var.def->type == def_int) { + int value = *ptr1->var.def->location.pi; + + value = value + 1; + if ((ptr1->var.def->minvalue != UL && + value < ptr1->var.def->minvalue)) + value = ptr1->var.def->minvalue; + if ((ptr1->var.def->maxvalue != UL && + value > ptr1->var.def->maxvalue)) + value = ptr1->var.def->maxvalue; + if (*ptr1->var.def->location.pi != value) + S_StartSound(NULL,sfx_pstop); + *ptr1->var.def->location.pi = value; + } + if (ptr1->var.def->type == def_str) { + int old_value, value; + + old_value = M_IndexInChoices(*ptr1->var.def->location.ppsz, + ptr1->selectstrings); + value = old_value + 1; + if (ptr1->selectstrings[value] == NULL) + value = old_value; + if (old_value != value) + S_StartSound(NULL,sfx_pstop); + *ptr1->var.def->location.ppsz = ptr1->selectstrings[value]; + } + } + if (ch == key_menu_enter) { + // phares 4/14/98: + // If not in demoplayback, demorecording, or netgame, + // and there's a second variable in var2, set that + // as well + + // killough 8/15/98: add warning messages + + if (ptr1->m_flags & (S_LEVWARN | S_PRGWARN)) + warn_about_changes(ptr1->m_flags & // killough 10/98 + (S_LEVWARN | S_PRGWARN)); + else + M_UpdateCurrent(ptr1->var.def); + + if (ptr1->action) // killough 10/98 + ptr1->action(); + M_SelectDone(ptr1); // phares 4/17/98 + } + return true; + } + + } + + // Key Bindings + + if (set_keybnd_active) // on a key binding setup screen + if (setup_select) // incoming key or button gets bound + { + if (ev->type == ev_joystick) + { + int oldbutton; + setup_group group; + dboolean search = true; + + if (!ptr1->m_joy) + return true; // not a legal action here (yet) + + // see if the button is already bound elsewhere. if so, you + // have to swap bindings so the action where it's currently + // bound doesn't go dead. Since there is more than one + // keybinding screen, you have to search all of them for + // any duplicates. You're only interested in the items + // that belong to the same group as the one you're changing. + + oldbutton = *ptr1->m_joy; + group = ptr1->m_group; + if (ev->data1 & 1) + ch = 0; + else if (ev->data1 & 2) + ch = 1; + else if (ev->data1 & 4) + ch = 2; + else if (ev->data1 & 8) + ch = 3; + else + return true; + for (i = 0 ; keys_settings[i] && search ; i++) + for (ptr2 = keys_settings[i] ; !(ptr2->m_flags & S_END) ; ptr2++) + if (ptr2->m_group == group && ptr1 != ptr2) + if (ptr2->m_flags & S_KEY && ptr2->m_joy) + if (*ptr2->m_joy == ch) + { + *ptr2->m_joy = oldbutton; + search = false; + break; + } + *ptr1->m_joy = ch; + } + else if (ev->type == ev_mouse) + { + int i,oldbutton; + setup_group group; + dboolean search = true; + + if (!ptr1->m_mouse) + return true; // not a legal action here (yet) + + // see if the button is already bound elsewhere. if so, you + // have to swap bindings so the action where it's currently + // bound doesn't go dead. Since there is more than one + // keybinding screen, you have to search all of them for + // any duplicates. You're only interested in the items + // that belong to the same group as the one you're changing. + + oldbutton = *ptr1->m_mouse; + group = ptr1->m_group; + if ((ch = GetButtons(MAX_MOUSEB, ev->data1)) == -1) + return true; + for (i = 0 ; keys_settings[i] && search ; i++) + for (ptr2 = keys_settings[i] ; !(ptr2->m_flags & S_END) ; ptr2++) + if (ptr2->m_group == group && ptr1 != ptr2) + if (ptr2->m_flags & S_KEY && ptr2->m_mouse) + if (*ptr2->m_mouse == ch) + { + *ptr2->m_mouse = oldbutton; + search = false; + break; + } + *ptr1->m_mouse = ch; + } + else // keyboard key + { + int i,oldkey; + setup_group group; + dboolean search = true; + + // see if 'ch' is already bound elsewhere. if so, you have + // to swap bindings so the action where it's currently + // bound doesn't go dead. Since there is more than one + // keybinding screen, you have to search all of them for + // any duplicates. You're only interested in the items + // that belong to the same group as the one you're changing. + + // if you find that you're trying to swap with an action + // that has S_KEEP set, you can't bind ch; it's already + // bound to that S_KEEP action, and that action has to + // keep that key. + + oldkey = *ptr1->var.m_key; + group = ptr1->m_group; + for (i = 0 ; keys_settings[i] && search ; i++) + for (ptr2 = keys_settings[i] ; !(ptr2->m_flags & S_END) ; ptr2++) + if (ptr2->m_flags & (S_KEY|S_KEEP) && + ptr2->m_group == group && + ptr1 != ptr2) + if (*ptr2->var.m_key == ch) + { + if (ptr2->m_flags & S_KEEP) + return true; // can't have it! + *ptr2->var.m_key = oldkey; + search = false; + break; + } + *ptr1->var.m_key = ch; + } + + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + // Weapons + + if (set_weapon_active) // on the weapons setup screen + if (setup_select) // changing an entry + { + if (ch != key_menu_enter) + { + ch -= '0'; // out of ascii + if (ch < 1 || ch > 9) + return true; // ignore + + // Plasma and BFG don't exist in shareware + // killough 10/98: allow it anyway, since this + // isn't the game itself, just setting preferences + + // see if 'ch' is already assigned elsewhere. if so, + // you have to swap assignments. + + // killough 11/98: simplified + + for (i = 0; (ptr2 = weap_settings[i]); i++) + for (; !(ptr2->m_flags & S_END); ptr2++) + if (ptr2->m_flags & S_WEAP && + *ptr2->var.def->location.pi == ch && ptr1 != ptr2) + { + *ptr2->var.def->location.pi = *ptr1->var.def->location.pi; + goto end; + } + end: + *ptr1->var.def->location.pi = ch; + } + + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + // Automap + + if (set_auto_active) // on the automap setup screen + if (setup_select) // incoming key + { + if (ch == key_menu_down) + { + if (++color_palette_y == 16) + color_palette_y = 0; + S_StartSound(NULL,sfx_itemup); + return true; + } + + if (ch == key_menu_up) + { + if (--color_palette_y < 0) + color_palette_y = 15; + S_StartSound(NULL,sfx_itemup); + return true; + } + + if (ch == key_menu_left) + { + if (--color_palette_x < 0) + color_palette_x = 15; + S_StartSound(NULL,sfx_itemup); + return true; + } + + if (ch == key_menu_right) + { + if (++color_palette_x == 16) + color_palette_x = 0; + S_StartSound(NULL,sfx_itemup); + return true; + } + + if (ch == key_menu_enter) + { + *ptr1->var.def->location.pi = color_palette_x + 16*color_palette_y; + M_SelectDone(ptr1); // phares 4/17/98 + colorbox_active = false; + return true; + } + } + + // killough 10/98: consolidate handling into one place: + if (setup_select && + set_enemy_active | set_general_active | set_chat_active | + set_mess_active | set_status_active | set_compat_active) + { + if (ptr1->m_flags & S_STRING) // creating/editing a string? + { + if (ch == key_menu_backspace) // backspace and DEL + { + if (chat_string_buffer[chat_index] == 0) + { + if (chat_index > 0) + chat_string_buffer[--chat_index] = 0; + } + // shift the remainder of the text one char left + else + strcpy(&chat_string_buffer[chat_index], + &chat_string_buffer[chat_index+1]); + } + else if (ch == key_menu_left) // move cursor left + { + if (chat_index > 0) + chat_index--; + } + else if (ch == key_menu_right) // move cursor right + { + if (chat_string_buffer[chat_index] != 0) + chat_index++; + } + else if ((ch == key_menu_enter) || + (ch == key_menu_escape)) + { + *ptr1->var.def->location.ppsz = chat_string_buffer; + M_SelectDone(ptr1); // phares 4/17/98 + } + + // Adding a char to the text. Has to be a printable + // char, and you can't overrun the buffer. If the + // chat string gets larger than what the screen can hold, + // it is dealt with when the string is drawn (above). + + else if ((ch >= 32) && (ch <= 126)) + if ((chat_index+1) < CHAT_STRING_BFR_SIZE) + { + if (shiftdown) + ch = shiftxform[ch]; + if (chat_string_buffer[chat_index] == 0) + { + chat_string_buffer[chat_index++] = ch; + chat_string_buffer[chat_index] = 0; + } + else + chat_string_buffer[chat_index++] = ch; + } + return true; + } + + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + // Not changing any items on the Setup screens. See if we're + // navigating the Setup menus or selecting an item to change. + + if (ch == key_menu_down) + { + ptr1->m_flags &= ~S_HILITE; // phares 4/17/98 + do + if (ptr1->m_flags & S_END) + { + set_menu_itemon = 0; + ptr1 = current_setup_menu; + } + else + { + set_menu_itemon++; + ptr1++; + } + while (ptr1->m_flags & S_SKIP); + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + if (ch == key_menu_up) + { + ptr1->m_flags &= ~S_HILITE; // phares 4/17/98 + do + { + if (set_menu_itemon == 0) + do + set_menu_itemon++; + while(!((current_setup_menu + set_menu_itemon)->m_flags & S_END)); + set_menu_itemon--; + } + while((current_setup_menu + set_menu_itemon)->m_flags & S_SKIP); + M_SelectDone(current_setup_menu + set_menu_itemon); // phares 4/17/98 + return true; + } + + if (ch == key_menu_clear) + { + if (ptr1->m_flags & S_KEY) + { + if (ptr1->m_joy) + *ptr1->m_joy = -1; + + if (ptr1->m_mouse) + *ptr1->m_mouse = -1; + + *ptr1->var.m_key = 0; + } + + return true; + } + + if (ch == key_menu_enter) + { + int flags = ptr1->m_flags; + + // You've selected an item to change. Highlight it, post a new + // message about what to do, and get ready to process the + // change. + // + // killough 10/98: use friendlier char-based input buffer + + if (flags & S_NUM) + { + setup_gather = true; + print_warning_about_changes = false; + gather_count = 0; + } + else if (flags & S_COLOR) + { + int color = *ptr1->var.def->location.pi; + + if (color < 0 || color > 255) // range check the value + color = 0; // 'no show' if invalid + + color_palette_x = *ptr1->var.def->location.pi & 15; + color_palette_y = *ptr1->var.def->location.pi >> 4; + colorbox_active = true; + } + else if (flags & S_STRING) + { + // copy chat string into working buffer; trim if needed. + // free the old chat string memory and replace it with + // the (possibly larger) new memory for editing purposes + // + // killough 10/98: fix bugs, simplify + + chat_string_buffer = malloc(CHAT_STRING_BFR_SIZE); + strncpy(chat_string_buffer, + *ptr1->var.def->location.ppsz, CHAT_STRING_BFR_SIZE); + + // guarantee null delimiter + chat_string_buffer[CHAT_STRING_BFR_SIZE-1] = 0; + + // set chat table pointer to working buffer + // and free old string's memory. + { + union { const char **c; char **s; } u; // type punning via unions + + u.c = ptr1->var.def->location.ppsz; + free(*(u.s)); + *(u.c) = chat_string_buffer; + } + chat_index = 0; // current cursor position in chat_string_buffer + } + else if (flags & S_RESET) + default_verify = true; + + ptr1->m_flags |= S_SELECT; + setup_select = true; + S_StartSound(NULL,sfx_itemup); + return true; + } + + if ((ch == key_menu_escape) || (ch == key_menu_backspace)) + { + M_SetSetupMenuItemOn(set_menu_itemon); + if (ch == key_menu_escape) // Clear all menus + M_ClearMenus(); + else // key_menu_backspace = return to Setup Menu + if (currentMenu->prevMenu) + { + currentMenu = currentMenu->prevMenu; + itemOn = currentMenu->lastOn; + S_StartSound(NULL,sfx_swtchn); + } + ptr1->m_flags &= ~(S_HILITE|S_SELECT);// phares 4/19/98 + setup_active = false; + set_keybnd_active = false; + set_weapon_active = false; + set_status_active = false; + set_auto_active = false; + set_enemy_active = false; + set_mess_active = false; + set_chat_active = false; + colorbox_active = false; + default_verify = false; // phares 4/19/98 + set_general_active = false; // killough 10/98 + set_compat_active = false; // killough 10/98 + HU_Start(); // catch any message changes // phares 4/19/98 + S_StartSound(NULL,sfx_swtchx); + return true; + } + + // Some setup screens may have multiple screens. + // When there are multiple screens, m_prev and m_next items need to + // be placed on the appropriate screen tables so the user can + // move among the screens using the left and right arrow keys. + // The m_var1 field contains a pointer to the appropriate screen + // to move to. + + if (ch == key_menu_left) + { + ptr2 = ptr1; + do + { + ptr2++; + if (ptr2->m_flags & S_PREV) + { + ptr1->m_flags &= ~S_HILITE; + mult_screens_index--; + M_SetSetupMenuItemOn(set_menu_itemon); + current_setup_menu = ptr2->var.menu; + set_menu_itemon = M_GetSetupMenuItemOn(); + print_warning_about_changes = false; // killough 10/98 + while (current_setup_menu[set_menu_itemon++].m_flags&S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; + S_StartSound(NULL,sfx_pstop); // killough 10/98 + return true; + } + } + while (!(ptr2->m_flags & S_END)); + } + + if (ch == key_menu_right) + { + ptr2 = ptr1; + do + { + ptr2++; + if (ptr2->m_flags & S_NEXT) + { + ptr1->m_flags &= ~S_HILITE; + mult_screens_index++; + M_SetSetupMenuItemOn(set_menu_itemon); + current_setup_menu = ptr2->var.menu; + set_menu_itemon = M_GetSetupMenuItemOn(); + print_warning_about_changes = false; // killough 10/98 + while (current_setup_menu[set_menu_itemon++].m_flags&S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; + S_StartSound(NULL,sfx_pstop); // killough 10/98 + return true; + } + } + while (!(ptr2->m_flags & S_END)); + } + + } // End of Setup Screen processing + + // From here on, these navigation keys are used on the BIG FONT menus + // like the Main Menu. + + if (ch == key_menu_down) // phares 3/7/98 + { + do + { + if (itemOn+1 > currentMenu->numitems-1) + itemOn = 0; + else + itemOn++; + S_StartSound(NULL,sfx_pstop); + } + while(currentMenu->menuitems[itemOn].status==-1); + return true; + } + + if (ch == key_menu_up) // phares 3/7/98 + { + do + { + if (!itemOn) + itemOn = currentMenu->numitems-1; + else + itemOn--; + S_StartSound(NULL,sfx_pstop); + } + while(currentMenu->menuitems[itemOn].status==-1); + return true; + } + + if (ch == key_menu_left) // phares 3/7/98 + { + if (currentMenu->menuitems[itemOn].routine && + currentMenu->menuitems[itemOn].status == 2) + { + S_StartSound(NULL,sfx_stnmov); + currentMenu->menuitems[itemOn].routine(0); + } + return true; + } + + if (ch == key_menu_right) // phares 3/7/98 + { + if (currentMenu->menuitems[itemOn].routine && + currentMenu->menuitems[itemOn].status == 2) + { + S_StartSound(NULL,sfx_stnmov); + currentMenu->menuitems[itemOn].routine(1); + } + return true; + } + + if (ch == key_menu_enter) // phares 3/7/98 + { + if (currentMenu->menuitems[itemOn].routine && + currentMenu->menuitems[itemOn].status) + { + currentMenu->lastOn = itemOn; + if (currentMenu->menuitems[itemOn].status == 2) + { + currentMenu->menuitems[itemOn].routine(1); // right arrow + S_StartSound(NULL,sfx_stnmov); + } + else + { + currentMenu->menuitems[itemOn].routine(itemOn); + S_StartSound(NULL,sfx_pistol); + } + } + //jff 3/24/98 remember last skill selected + // killough 10/98 moved to skill-specific functions + return true; + } + + if (ch == key_menu_escape) // phares 3/7/98 + { + currentMenu->lastOn = itemOn; + M_ClearMenus (); + S_StartSound(NULL,sfx_swtchx); + return true; + } + + if (ch == key_menu_backspace) // phares 3/7/98 + { + currentMenu->lastOn = itemOn; + + // phares 3/30/98: + // add checks to see if you're in the extended help screens + // if so, stay with the same menu definition, but bump the + // index back one. if the index bumps back far enough ( == 0) + // then you can return to the Read_Thisn menu definitions + + if (currentMenu->prevMenu) + { + if (currentMenu == &ExtHelpDef) + { + if (--extended_help_index == 0) + { + currentMenu = currentMenu->prevMenu; + extended_help_index = 1; // reset + } + } + else + currentMenu = currentMenu->prevMenu; + itemOn = currentMenu->lastOn; + S_StartSound(NULL,sfx_swtchn); + } + return true; + } + + // [FG] delete a savegame + + else if (ch == key_menu_clear) + { + if (currentMenu == &LoadDef || currentMenu == &SaveDef) + { + if (LoadMenue[itemOn].status) + { + S_StartSound(NULL,sfx_itemup); + currentMenu->lastOn = itemOn; + delete_verify = true; + return true; + } + else + { + S_StartSound(NULL,sfx_oof); + } + } + } + + else + { + for (i = itemOn+1;i < currentMenu->numitems;i++) + if (currentMenu->menuitems[i].alphaKey == ch) + { + itemOn = i; + S_StartSound(NULL,sfx_pstop); + return true; + } + for (i = 0;i <= itemOn;i++) + if (currentMenu->menuitems[i].alphaKey == ch) + { + itemOn = i; + S_StartSound(NULL,sfx_pstop); + return true; + } + } + return false; +} + +// +// End of M_Responder +// +///////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// +// General Routines +// +// This displays the Main menu and gets the menu screens rolling. +// Plus a variety of routines that control the Big Font menu display. +// Plus some initialization for game-dependant situations. + +void M_StartControlPanel (void) +{ + // intro might call this repeatedly + + if (menuactive) + return; + + //jff 3/24/98 make default skill menu choice follow -skill or defaultskill + //from command line or config file + // + // killough 10/98: + // Fix to make "always floating" with menu selections, and to always follow + // defaultskill, instead of -skill. + + NewDef.lastOn = defaultskill - 1; + + // e6y + // We need to remove the fourth episode for pre-ultimate complevels. + // It is located here instead of M_Init() because of TNTCOMP cheat. + if (!EpiCustom) + { + EpiDef.numitems = ep_end; + if (gamemode != commercial + && (compatibility_level < ultdoom_compatibility + || W_CheckNumForName(EpiDef.menuitems[ep4].name) == -1)) + { + EpiDef.numitems--; + } + } + + default_verify = 0; // killough 10/98 + menuactive = mnact_float; + currentMenu = &MainDef; // JDC + itemOn = currentMenu->lastOn; // JDC + print_warning_about_changes = false; // killough 11/98 +} + +// +// M_Drawer +// Called after the view has been rendered, +// but before it has been blitted. +// +// killough 9/29/98: Significantly reformatted source +// + +void M_Drawer (void) +{ + inhelpscreens = false; + + // Horiz. & Vertically center string and print it. + // killough 9/29/98: simplified code, removed 40-character width limit + if (messageToPrint) + { + /* cph - strdup string to writable memory */ + char *ms = strdup(messageString); + char *p = ms; + + int y = 100 - M_StringHeight(messageString)/2; + while (*p) + { + char *string = p, c; + while ((c = *p) && *p != '\n') + p++; + *p = 0; + M_WriteText(160 - M_StringWidth(string)/2, y, string, CR_DEFAULT); + y += hu_font[0].height; + if ((*p = c)) + p++; + } + free(ms); + } + else + if (menuactive) + { + int x,y,max,i; + int lumps_missing = 0; + + menuactive = mnact_float; // Boom-style menu drawers will set mnact_full + + if (currentMenu->routine) + currentMenu->routine(); // call Draw routine + + // DRAW MENU + + x = currentMenu->x; + y = currentMenu->y; + max = currentMenu->numitems; + + for (i = 0; i < max; i++) + if (currentMenu->menuitems[i].name[0]) + if (W_CheckNumForName(currentMenu->menuitems[i].name) < 0) + lumps_missing++; + + if (lumps_missing == 0) + for (i=0;imenuitems[i].name[0]) + V_DrawNamePatch(x,y,0,currentMenu->menuitems[i].name, + CR_DEFAULT, VPT_STRETCH); + y += LINEHEIGHT; + } + else + for (i = 0; i < max; i++) + { + const char *alttext = currentMenu->menuitems[i].alttext; + if (alttext) + M_WriteText(x, y+8-(M_StringHeight(alttext)/2), alttext, CR_DEFAULT); + y += LINEHEIGHT; + } + + // DRAW SKULL + + // CPhipps - patch drawing updated + V_DrawNamePatch(x + SKULLXOFF, currentMenu->y - 5 + itemOn*LINEHEIGHT,0, + skullName[whichSkull], CR_DEFAULT, VPT_STRETCH); + } +} + +// +// M_ClearMenus +// +// Called when leaving the menu screens for the real world + +void M_ClearMenus (void) +{ + menuactive = mnact_inactive; + print_warning_about_changes = 0; // killough 8/15/98 + default_verify = 0; // killough 10/98 + + BorderNeedRefresh = true; + + // if (!netgame && usergame && paused) + // sendpause = true; +} + +// +// M_SetupNextMenu +// +void M_SetupNextMenu(menu_t *menudef) +{ + currentMenu = menudef; + itemOn = currentMenu->lastOn; + + BorderNeedRefresh = true; +} + +///////////////////////////// +// +// M_Ticker +// +void M_Ticker (void) +{ + if (--skullAnimCounter <= 0) + { + whichSkull ^= 1; + skullAnimCounter = 8; + } +} + +///////////////////////////// +// +// Message Routines +// + +void M_StartMessage (const char* string,void* routine,dboolean input) +{ + messageLastMenuActive = menuactive; + messageToPrint = 1; + messageString = string; + messageRoutine = routine; + messageNeedsInput = input; + menuactive = mnact_float; + return; +} + +void M_StopMessage(void) +{ + menuactive = messageLastMenuActive; + messageToPrint = 0; +} + +///////////////////////////// +// +// Thermometer Routines +// + +// +// M_DrawThermo draws the thermometer graphic for Mouse Sensitivity, +// Sound Volume, etc. +// +// proff/nicolas 09/20/98 -- changed for hi-res +// CPhipps - patch drawing updated +// +void M_DrawThermo(int x,int y,int thermWidth,int thermDot ) +{ + int xx; + int i; + /* + * Modification By Barry Mead to allow the Thermometer to have vastly + * larger ranges. (the thermWidth parameter can now have a value as + * large as 200. Modified 1-9-2000 Originally I used it to make + * the sensitivity range for the mouse better. It could however also + * be used to improve the dynamic range of music and sound affect + * volume controls for example. + */ + int horizScaler; //Used to allow more thermo range for mouse sensitivity. + thermWidth = (thermWidth > 200) ? 200 : thermWidth; //Clamp to 200 max + horizScaler = (thermWidth > 23) ? (200 / thermWidth) : 8; //Dynamic range + xx = x; + V_DrawNamePatch(xx, y, 0, "M_THERML", CR_DEFAULT, VPT_STRETCH); + xx += 8; + for (i=0;ix - 10, menu->y+item*LINEHEIGHT - 1, 0, + "M_CELL1", CR_DEFAULT, VPT_STRETCH); +} + +// +// Draw a full cell in the thermometer +// + +void M_DrawSelCell (menu_t* menu,int item) +{ + // CPhipps - patch drawing updated + V_DrawNamePatch(menu->x - 10, menu->y+item*LINEHEIGHT - 1, 0, + "M_CELL2", CR_DEFAULT, VPT_STRETCH); +} + +///////////////////////////// +// +// String-drawing Routines +// + +// +// Find string width from hu_font chars +// + +int M_StringWidth(const char* string) +{ + int i, c, w = 0; + for (i = 0;(size_t)i < strlen(string);i++) + w += (c = toupper(string[i]) - HU_FONTSTART) < 0 || c >= HU_FONTSIZE ? + 4 : hu_font[c].width; + return w; +} + +// +// Find string height from hu_font chars +// + +int M_StringHeight(const char* string) +{ + int i, h, height = h = hu_font[0].height; + for (i = 0;string[i];i++) // killough 1/31/98 + if (string[i] == '\n') + h += height; + return h; +} + +// +// Write a string using the hu_font +// +void M_WriteText (int x,int y, const char* string, int cm) +{ + int w; + const char* ch; + int c; + int cx; + int cy; + int flags; + + ch = string; + cx = x; + cy = y; + + flags = VPT_STRETCH; + if (cm != CR_DEFAULT) + flags |= VPT_TRANS; + + 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 = hu_font[c].width; + if (cx+w > BASE_WIDTH) + break; + // proff/nicolas 09/20/98 -- changed for hi-res + // CPhipps - patch drawing updated + V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, cm, flags); + cx+=w; + } +} + +void M_DrawTitle(int x, int y, const char *patch, int cm, + const char *alttext, int altcm) +{ + int lumpnum = W_CheckNumForName(patch); + + if (lumpnum >= 0) + { + int flags = VPT_STRETCH; + if (cm != CR_DEFAULT) + flags |= VPT_TRANS; + V_DrawNumPatch(x, y, 0, lumpnum, cm, flags); + } + else + { + // patch doesn't exist, draw some text in place of it + M_WriteText(160-(M_StringWidth(alttext)/2), + y+8-(M_StringHeight(alttext)/2), // assumes patch height 16 + alttext, altcm); + } +} + +///////////////////////////// +// +// Initialization Routines to take care of one-time setup +// + +// phares 4/08/98: +// M_InitHelpScreen() clears the weapons from the HELP +// screen that don't exist in this version of the game. + +void M_InitHelpScreen(void) +{ + setup_menu_t* src; + + src = helpstrings; + while (!(src->m_flags & S_END)) { + + if ((strncmp(src->m_text,"PLASMA",6) == 0) && (gamemode == shareware)) + src->m_flags = S_SKIP; // Don't show setting or item + if ((strncmp(src->m_text,"BFG",3) == 0) && (gamemode == shareware)) + src->m_flags = S_SKIP; // Don't show setting or item + if ((strncmp(src->m_text,"SSG",3) == 0) && (gamemode != commercial)) + src->m_flags = S_SKIP; // Don't show setting or item + src++; + } +} + +// +// M_Init +// +void M_Init(void) +{ + M_InitDefaults(); // killough 11/98 + currentMenu = &MainDef; + menuactive = mnact_inactive; + itemOn = currentMenu->lastOn; + whichSkull = 0; + skullAnimCounter = 10; + screenSize = screenblocks - 3; + messageToPrint = 0; + messageString = NULL; + messageLastMenuActive = menuactive; + quickSaveSlot = -1; + + // Here we could catch other version dependencies, + // like HELP1/2, and four episodes. + + switch(gamemode) + { + case commercial: + // This is used because DOOM 2 had only one HELP + // page. I use CREDIT as second page now, but + // kept this hack for educational purposes. + MainMenu[readthis] = MainMenu[quitdoom]; + MainDef.numitems--; + MainDef.y += 8; + if (!EpiCustom) + NewDef.prevMenu = &MainDef; + ReadDef1.routine = M_DrawReadThis1; + ReadDef1.x = 330; + ReadDef1.y = 165; + ReadMenu1[0].routine = M_FinishReadThis; + break; + case registered: + // Episode 2 and 3 are handled, + // branching to an ad screen. + + // killough 2/21/98: Fix registered Doom help screen + // killough 10/98: moved to second screen, moved up to the top + ReadDef2.y = 15; + // fallthrough + + case shareware: + // We need to remove the fourth episode. + EpiDef.numitems--; + break; + case retail: + // We are fine. + default: + break; + } + + M_InitHelpScreen(); // init the help screen // phares 4/08/98 + M_InitExtendedHelp(); // init extended help screens // phares 3/30/98 + + //e6y + M_ChangeSpeed(); + M_ChangeMaxViewPitch(); + M_ChangeMouseLook(); + M_ChangeMouseInvert(); + M_ChangeFOV(); +#ifdef GL_DOOM + M_ChangeSpriteClip(); + M_ChangeAllowBoomColormaps(); +#endif + + M_ChangeDemoSmoothTurns(); + M_ChangeDemoExtendedFormat(); + + M_ChangeMapMultisamling(); + + render_stretch_hud = render_stretch_hud_default; + + M_ChangeMIDIPlayer(); +} + +// +// End of General Routines +// +///////////////////////////////////////////////////////////////////////////// diff --git a/src/m_menu.h b/src/m_menu.h new file mode 100644 index 0000000..c2b5a0c --- /dev/null +++ b/src/m_menu.h @@ -0,0 +1,187 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Menu widget stuff, episode selection and such. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __M_MENU__ +#define __M_MENU__ + +#include "d_event.h" + +// +// MENUS +// +// Called by main loop, +// saves config file and calls I_Quit when user exits. +// Even when the menu is not displayed, +// this can resize the view and change game parameters. +// Does all the real work of the menu interaction. + +dboolean M_Responder (event_t *ev); + +// Called by main loop, +// only used for menu (skull cursor) animation. + +void M_Ticker (void); + +// Called by main loop, +// draws the menus directly into the screen buffer. + +void M_Drawer (void); + +// Called by D_DoomMain, +// loads the config file. + +void M_Init (void); + +// Called by intro code to force menu up upon a keypress, +// does nothing if menu is already up. + +void M_StartControlPanel (void); + +void M_ForcedLoadGame(const char *msg); // killough 5/15/98: forced loadgames + +void M_Trans(void); // killough 11/98: reset translucency + +void M_ResetMenu(void); // killough 11/98: reset main menu ordering + +void M_DrawCredits(void); // killough 11/98 + +/* killough 8/15/98: warn about changes not being committed until next game */ +#define warn_about_changes(x) (warning_about_changes=(x), \ + print_warning_about_changes = 2) + +extern int warning_about_changes, print_warning_about_changes; + +extern dboolean menu_background; + +/**************************** + * + * The following #defines are for the m_flags field of each item on every + * Setup Screen. They can be OR'ed together where appropriate + */ + +#define S_HILITE 0x1 // Cursor is sitting on this item +#define S_SELECT 0x2 // We're changing this item +#define S_TITLE 0x4 // Title item +#define S_YESNO 0x8 // Yes or No item +#define S_CRITEM 0x10 // Message color +#define S_COLOR 0x20 // Automap color +#define S_CHAT 0x40 // Chat String +#define S_RESET 0x80 // Reset to Defaults Button +#define S_PREV 0x100 // Previous menu exists +#define S_NEXT 0x200 // Next menu exists +#define S_KEY 0x400 // Key Binding +#define S_WEAP 0x800 // Weapon # +#define S_NUM 0x1000 // Numerical item +#define S_SKIP 0x2000 // Cursor can't land here +#define S_KEEP 0x4000 // Don't swap key out +#define S_END 0x8000 // Last item in list (dummy) +#define S_LEVWARN 0x10000// killough 8/30/98: Always warn about pending change +#define S_PRGWARN 0x20000// killough 10/98: Warn about change until next run +#define S_BADVAL 0x40000// killough 10/98: Warn about bad value +#define S_FILE 0x80000// killough 10/98: Filenames +#define S_LEFTJUST 0x100000 // killough 10/98: items which are left-justified +#define S_CREDIT 0x200000 // killough 10/98: credit +#define S_BADVID 0x400000 // killough 12/98: video mode change error +#define S_CHOICE 0x800000 // this item has several values + +//e6y +#define S_DISABLE 0x1000000 + +/* S_SHOWDESC = the set of items whose description should be displayed + * S_SHOWSET = the set of items whose setting should be displayed + * S_STRING = the set of items whose settings are strings -- killough 10/98: + * S_HASDEFPTR = the set of items whose var field points to default array + */ + +#define S_SHOWDESC (S_TITLE|S_YESNO|S_CRITEM|S_COLOR|S_CHAT|S_RESET|S_PREV|S_NEXT|S_KEY|S_WEAP|S_NUM|S_FILE|S_CREDIT|S_CHOICE) + +#define S_SHOWSET (S_YESNO|S_CRITEM|S_COLOR|S_CHAT|S_KEY|S_WEAP|S_NUM|S_FILE|S_CHOICE) + +#define S_STRING (S_CHAT|S_FILE) + +#define S_HASDEFPTR (S_STRING|S_YESNO|S_NUM|S_WEAP|S_COLOR|S_CRITEM|S_CHOICE) + +/**************************** + * + * The setup_group enum is used to show which 'groups' keys fall into so + * that you can bind a key differently in each 'group'. + */ + +typedef enum { + m_null, // Has no meaning; not applicable + m_scrn, // A key can not be assigned to more than one action + m_map, // in the same group. A key can be assigned to one + m_menu, // action in one group, and another action in another. +} setup_group; + +/**************************** + * + * phares 4/17/98: + * State definition for each item. + * This is the definition of the structure for each setup item. Not all + * fields are used by all items. + * + * A setup screen is defined by an array of these items specific to + * that screen. + * + * killough 11/98: + * + * Restructured to allow simpler table entries, + * and to Xref with defaults[] array in m_misc.c. + * Moved from m_menu.c to m_menu.h so that m_misc.c can use it. + */ + +typedef struct setup_menu_s +{ + const char *m_text; /* text to display */ + int m_flags; /* phares 4/17/98: flag bits S_* (defined above) */ + setup_group m_group; /* Group */ + short m_x; /* screen x position (left is 0) */ + short m_y; /* screen y position (top is 0) */ + + union /* killough 11/98: The first field is a union of several types */ + { + const void *var; /* generic variable */ + int *m_key; /* key value, or 0 if not shown */ + const char *name; /* name */ + struct default_s *def; /* default[] table entry */ + struct setup_menu_s *menu; /* next or prev menu */ + } var; + + int *m_mouse; /* mouse button value, or 0 if not shown */ + int *m_joy; /* joystick button value, or 0 if not shown */ + void (*action)(void); /* killough 10/98: function to call after changing */ + const char **selectstrings; /* list of strings for choice value */ +} setup_menu_t; + +#endif diff --git a/src/m_misc.c b/src/m_misc.c new file mode 100644 index 0000000..dc35ac9 --- /dev/null +++ b/src/m_misc.c @@ -0,0 +1,1946 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Main loop menu stuff. + * Default Config File. + * PCX Screenshots. + * + *-----------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef _MSC_VER +#include +#endif +#include +#include + +#include "doomstat.h" +#include "m_argv.h" +#include "g_game.h" +#include "m_menu.h" +#include "am_map.h" +#include "w_wad.h" +#include "i_system.h" +#include "i_sound.h" +#include "i_video.h" +#include "v_video.h" +#include "hu_stuff.h" +#include "st_stuff.h" +#include "dstrings.h" +#include "m_misc.h" +#include "s_sound.h" +#include "sounds.h" +#include "i_joy.h" +#include "lprintf.h" +#include "d_main.h" +#include "d_deh.h" +#include "r_draw.h" +#include "r_demo.h" +#include "r_fps.h" +#include "r_main.h" +#include "r_things.h" +#include "r_sky.h" + +//e6y +#include "gl_struct.h" +#include "g_overflow.h" +#include "e6y.h" +#ifdef USE_WINDOWS_LAUNCHER +#include "e6y_launcher.h" +#endif + +// NSM +#include "i_capture.h" + +#include "m_io.h" + +/* cph - disk icon not implemented */ +static inline void I_BeginRead(void) {} +static inline void I_EndRead(void) {} + +/* + * M_WriteFile + * + * killough 9/98: rewritten to use stdio and to flash disk icon + */ + +dboolean M_WriteFile(char const *name, const void *source, size_t length) +{ + FILE *fp; + + errno = 0; + + if (!(fp = M_fopen(name, "wb"))) // Try opening file + return 0; // Could not open file for writing + + I_BeginRead(); // Disk icon on + length = fwrite(source, 1, length, fp) == (size_t)length; // Write data + fclose(fp); + I_EndRead(); // Disk icon off + + if (!length) // Remove partially written file + M_remove(name); + + return length; +} + +/* + * M_ReadFile + * + * killough 9/98: rewritten to use stdio and to flash disk icon + */ + +int M_ReadFile(char const *name, byte **buffer) +{ + FILE *fp; + + if ((fp = M_fopen(name, "rb"))) + { + size_t length; + + I_BeginRead(); + fseek(fp, 0, SEEK_END); + length = ftell(fp); + fseek(fp, 0, SEEK_SET); + *buffer = Z_Malloc(length, PU_STATIC, 0); + if (fread(*buffer, 1, length, fp) == length) + { + fclose(fp); + I_EndRead(); + return length; + } + fclose(fp); + } + + /* cph 2002/08/10 - this used to return 0 on error, but that's ambiguous, + * because we could have a legit 0-length file. So make it -1. */ + return -1; +} + +// +// DEFAULTS +// + +int usemouse; +dboolean precache = true; /* if true, load all graphics at start */ + +// The available anisotropic +typedef enum { + gl_anisotropic_off = 0, + gl_anisotropic_2x = 1, + gl_anisotropic_4x = 2, + gl_anisotropic_8x = 3, + gl_anisotropic_16x = 4, +} gl_anisotropic_mode_t; + +extern int viewwidth; +extern int viewheight; +#ifdef GL_DOOM +extern int gl_nearclip; +extern int gl_colorbuffer_bits; +extern int gl_depthbuffer_bits; +extern int gl_texture_filter; +extern int gl_sprite_filter; +extern int gl_patch_filter; +extern int gl_texture_filter_anisotropic; +extern const char *gl_tex_format_string; +extern int gl_sky_detail; +extern int gl_use_paletted_texture; +extern int gl_use_shared_texture_palette; + +//e6y: all OpenGL extentions will be disabled with TRUE +extern int gl_compatibility; + +//cfg values +extern int gl_ext_texture_filter_anisotropic_default; +extern int gl_arb_texture_non_power_of_two_default; +extern int gl_arb_multitexture_default; +extern int gl_arb_texture_compression_default; +extern int gl_ext_framebuffer_object_default; +extern int gl_ext_packed_depth_stencil_default; +extern int gl_ext_blend_color_default; +extern int gl_use_stencil_default; +extern int gl_ext_arb_vertex_buffer_object_default; +extern int gl_arb_pixel_buffer_object_default; +extern int gl_arb_shader_objects_default; + +//e6y: motion bloor +extern int gl_motionblur; + +//e6y: fog +extern int gl_fog; +extern int gl_fog_color; + +extern int gl_finish; +extern int gl_clear; +extern int gl_ztrick; +#else +// dummy variables for !GL_DOOM +static int gl_nearclip; +extern int gl_colorbuffer_bits; +extern int gl_depthbuffer_bits; +static int gl_texture_filter; +static int gl_sprite_filter; +static int gl_patch_filter; +static int gl_texture_filter_anisotropic; +static const char *gl_tex_format_string; +static int gl_sky_detail; +static int gl_use_paletted_texture; +static int gl_use_shared_texture_palette; +static int gl_compatibility; +static int gl_ext_texture_filter_anisotropic_default; +static int gl_arb_texture_non_power_of_two_default; +static int gl_arb_multitexture_default; +static int gl_arb_texture_compression_default; +static int gl_ext_framebuffer_object_default; +static int gl_ext_packed_depth_stencil_default; +static int gl_ext_blend_color_default; +static int gl_use_stencil_default; +static int gl_ext_arb_vertex_buffer_object_default; +static int gl_arb_pixel_buffer_object_default; +static int gl_arb_shader_objects_default; +static int gl_motionblur; +static int gl_fog; +static int gl_fog_color; +static int gl_finish; +static int gl_clear; +static int gl_ztrick; + +// dummy variables for !GL_DOOM declared in gl_struct.h +int gl_use_display_lists; +int gl_sprite_offset_default; +int gl_sprite_blend; +int gl_mask_sprite_threshold; +int gl_skymode; +int gl_allow_detail_textures; +int gl_detail_maxdist; +spriteclipmode_t gl_spriteclip; +int gl_spriteclip_threshold; +int gl_sprites_frustum_culling; +int gl_boom_colormaps_default; +int gl_hires_24bit_colormap; +int gl_texture_internal_hires; +int gl_texture_external_hires; +int gl_hires_override_pwads; +const char *gl_texture_hires_dir; +int gl_texture_hqresize; +int gl_texture_hqresize_textures; +int gl_texture_hqresize_sprites; +int gl_texture_hqresize_patches; +motion_blur_params_t motion_blur; +gl_lightmode_t gl_lightmode_default; +int gl_light_ambient; +int useglgamma; +int gl_color_mip_levels; +simple_shadow_params_t simple_shadows; +int gl_shadows_maxdist; +int gl_shadows_factor; +int gl_blend_animations; +spritefuzzmode_t gl_thingspritefuzzmode; +spritefuzzmode_t gl_weaponspritefuzzmode; + +#endif + +extern int realtic_clock_rate; // killough 4/13/98: adjustable timer +extern int tran_filter_pct; // killough 2/21/98 + +extern int screenblocks; +extern int showMessages; + +#ifndef DJGPP +int mus_pause_opt; // 0 = kill music, 1 = pause, 2 = continue +#endif + +extern const char* chat_macros[]; + +extern int endoom_mode; + +extern const char* S_music_files[]; // cournia + +/* cph - Some MBF stuff parked here for now + * killough 10/98 + */ +int map_point_coordinates; +int map_level_stat; + +default_t defaults[] = +{ + //e6y + {"System settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"process_priority", {&process_priority},{0},0,2,def_int,ss_none}, + + {"Misc settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"default_compatibility_level",{(int*)&default_compatibility_level}, + {-1},-1,MAX_COMPATIBILITY_LEVEL-1, + def_int,ss_none}, // compatibility level" - CPhipps + {"vanilla_keymap",{&vanilla_keymap},{0},0,1, + def_bool,ss_none}, // Use vanilla keybaord mapping + {"realtic_clock_rate",{&realtic_clock_rate},{100},0,UL, + def_int,ss_none}, // percentage of normal speed (35 fps) realtic clock runs at + {"menu_background", {(int*)&menu_background}, {1}, 0, 1, + def_bool,ss_none}, // do Boom fullscreen menus have backgrounds? + {"max_player_corpse", {&bodyquesize}, {32},-1,UL, // killough 2/8/98 + def_int,ss_none}, // number of dead bodies in view supported (-1 = no limit) + {"flashing_hom",{&flashing_hom},{0},0,1, + def_bool,ss_none}, // killough 10/98 - enable flashing HOM indicator + {"demo_insurance",{&default_demo_insurance},{2},0,2, // killough 3/31/98 + def_int,ss_none}, // 1=take special steps ensuring demo sync, 2=only during recordings + {"endoom_mode", {&endoom_mode},{5},0,7, // CPhipps - endoom flags + def_hex, ss_none}, // 0, +1 for colours, +2 for non-ascii chars, +4 for skip-last-line + {"level_precache",{(int*)&precache},{1},0,1, + def_bool,ss_none}, // precache level data? + {"demo_smoothturns", {&demo_smoothturns}, {0},0,1, + def_bool,ss_stat}, + {"demo_smoothturnsfactor", {&demo_smoothturnsfactor}, {6},1,SMOOTH_PLAYING_MAXFACTOR, + def_int,ss_stat}, + {"boom_autoswitch", {(int*)&boom_autoswitch}, {1}, 0, 1, def_bool, ss_none}, + + {"Files",{NULL},{0},UL,UL,def_none,ss_none}, + /* cph - MBF-like wad/deh/bex autoload code */ + {"wadfile_1",{NULL,&wad_files[1]},{0,""},UL,UL,def_str,ss_none}, + {"wadfile_2",{NULL,&wad_files[2]},{0,""},UL,UL,def_str,ss_none}, + {"dehfile_1",{NULL,&deh_files[0]},{0,""},UL,UL,def_str,ss_none}, + {"dehfile_2",{NULL,&deh_files[1]},{0,""},UL,UL,def_str,ss_none}, + + {"Game settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"default_skill",{&defaultskill},{3},1,5, // jff 3/24/98 allow default skill setting + def_int,ss_none}, // selects default skill 1=TYTD 2=NTR 3=HMP 4=UV 5=NM + {"weapon_recoil",{&default_weapon_recoil},{0},0,1, + def_bool,ss_weap, &weapon_recoil}, + {"player_bobbing",{&default_player_bobbing},{1},0,1, // phares 2/25/98 + def_bool,ss_weap, &player_bobbing}, + {"weapon_attack_alignment",{&weapon_attack_alignment},{0},0,3, // phares 2/25/98 + def_int,ss_weap, &weapon_attack_alignment}, + {"monsters_remember",{&default_monsters_remember},{1},0,1, // killough 3/1/98 + def_bool,ss_enem, &monsters_remember}, + /* MBF AI enhancement options */ + {"monster_infighting",{&default_monster_infighting}, {1}, 0, 1, + def_bool, ss_enem, &monster_infighting}, + {"monster_backing",{&default_monster_backing}, {0}, 0, 1, + def_bool, ss_enem, &monster_backing}, + {"monster_avoid_hazards",{&default_monster_avoid_hazards}, {1}, 0, 1, + def_bool, ss_enem, &monster_avoid_hazards}, + {"monkeys",{&default_monkeys}, {0}, 0, 1, + def_bool, ss_enem, &monkeys}, + {"monster_friction",{&default_monster_friction}, {1}, 0, 1, + def_bool, ss_enem, &monster_friction}, + {"help_friends",{&default_help_friends}, {0}, 0, 1, + def_bool, ss_enem, &help_friends}, + {"allow_pushers",{&default_allow_pushers},{1},0,1, + def_bool,ss_weap, &allow_pushers}, + {"variable_friction",{&default_variable_friction},{1},0,1, + def_bool,ss_weap, &variable_friction}, + {"player_helpers",{&default_dogs}, {0}, 0, 3, + def_bool, ss_enem }, + {"friend_distance",{&default_distfriend}, {128}, 0, 999, + def_int, ss_enem, &distfriend}, + {"dog_jumping",{&default_dog_jumping}, {1}, 0, 1, + def_bool, ss_enem, &dog_jumping}, + /* End of MBF AI extras */ + + // [FG] colored blood and gibs + {"colored_blood",{(int*)&colored_blood}, {0}, 0, 1, def_bool, ss_none}, + + {"sts_always_red",{&sts_always_red},{1},0,1, // no color changes on status bar + def_bool,ss_stat}, + {"sts_pct_always_gray",{&sts_pct_always_gray},{0},0,1, // 2/23/98 chg default + def_bool,ss_stat}, // makes percent signs on status bar always gray + {"sts_traditional_keys",{&sts_traditional_keys},{0},0,1, // killough 2/28/98 + def_bool,ss_stat}, // disables doubled card and skull key display on status bar + {"sts_armorcolor_type",{&sts_armorcolor_type},{1},0,1, // armor color depends on type + def_bool,ss_stat}, + {"show_messages",{&showMessages},{1},0,1, + def_bool,ss_none}, // enables message display + {"autorun",{&autorun},{1},0,1, // killough 3/6/98: preserve autorun across games + def_bool,ss_none}, + + {"Dehacked settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"deh_apply_cheats",{&deh_apply_cheats},{1},0,1, + def_bool,ss_stat}, // if 0, dehacked cheat replacements are ignored. + + {"Compatibility settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"comp_zombie",{&default_comp[comp_zombie]},{1},0,1,def_bool,ss_comp,&comp[comp_zombie]}, + {"comp_infcheat",{&default_comp[comp_infcheat]},{0},0,1,def_bool,ss_comp,&comp[comp_infcheat]}, + {"comp_stairs",{&default_comp[comp_stairs]},{0},0,1,def_bool,ss_comp,&comp[comp_stairs]}, + {"comp_telefrag",{&default_comp[comp_telefrag]},{0},0,1,def_bool,ss_comp,&comp[comp_telefrag]}, + {"comp_dropoff",{&default_comp[comp_dropoff]},{0},0,1,def_bool,ss_comp,&comp[comp_dropoff]}, + {"comp_falloff",{&default_comp[comp_falloff]},{0},0,1,def_bool,ss_comp,&comp[comp_falloff]}, + {"comp_staylift",{&default_comp[comp_staylift]},{0},0,1,def_bool,ss_comp,&comp[comp_staylift]}, + {"comp_doorstuck",{&default_comp[comp_doorstuck]},{0},0,1,def_bool,ss_comp,&comp[comp_doorstuck]}, + {"comp_pursuit",{&default_comp[comp_pursuit]},{0},0,1,def_bool,ss_comp,&comp[comp_pursuit]}, + {"comp_vile",{&default_comp[comp_vile]},{0},0,1,def_bool,ss_comp,&comp[comp_vile]}, + {"comp_pain",{&default_comp[comp_pain]},{0},0,1,def_bool,ss_comp,&comp[comp_pain]}, + {"comp_skull",{&default_comp[comp_skull]},{0},0,1,def_bool,ss_comp,&comp[comp_skull]}, + {"comp_blazing",{&default_comp[comp_blazing]},{0},0,1,def_bool,ss_comp,&comp[comp_blazing]}, + {"comp_doorlight",{&default_comp[comp_doorlight]},{0},0,1,def_bool,ss_comp,&comp[comp_doorlight]}, + {"comp_god",{&default_comp[comp_god]},{0},0,1,def_bool,ss_comp,&comp[comp_god]}, + {"comp_skymap",{&default_comp[comp_skymap]},{0},0,1,def_bool,ss_comp,&comp[comp_skymap]}, + {"comp_floors",{&default_comp[comp_floors]},{0},0,1,def_bool,ss_comp,&comp[comp_floors]}, + {"comp_model",{&default_comp[comp_model]},{0},0,1,def_bool,ss_comp,&comp[comp_model]}, + {"comp_zerotags",{&default_comp[comp_zerotags]},{0},0,1,def_bool,ss_comp,&comp[comp_zerotags]}, + {"comp_moveblock",{&default_comp[comp_moveblock]},{0},0,1,def_bool,ss_comp,&comp[comp_moveblock]}, + {"comp_sound",{&default_comp[comp_sound]},{0},0,1,def_bool,ss_comp,&comp[comp_sound]}, + {"comp_666",{&default_comp[comp_666]},{0},0,1,def_bool,ss_comp,&comp[comp_666]}, + {"comp_soul",{&default_comp[comp_soul]},{0},0,1,def_bool,ss_comp,&comp[comp_soul]}, + {"comp_maskedanim",{&default_comp[comp_maskedanim]},{0},0,1,def_bool,ss_comp,&comp[comp_maskedanim]}, + //e6y + {"PrBoom-plus compatibility settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"comp_ouchface",{&default_comp[comp_ouchface]},{0},0,1,def_bool,ss_comp,&comp[comp_ouchface]}, + {"comp_maxhealth",{&default_comp[comp_maxhealth]},{0},0,1,def_bool,ss_comp,&comp[comp_maxhealth]}, + {"comp_translucency",{&default_comp[comp_translucency]},{0},0,1,def_bool,ss_comp,&comp[comp_translucency]}, + // [FG] allow MBF sky transfers in all complevels + {"comp_skytransfers",{&comp_skytransfers},{0},0,1,def_bool,ss_comp}, + + {"Sound settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"snd_pcspeaker",{&snd_pcspeaker},{0}, 0, 1, def_bool,ss_none}, + {"sound_card",{&snd_card},{-1},-1,7, // jff 1/18/98 allow Allegro drivers + def_int,ss_none}, // select sounds driver (DOS), -1 is autodetect, 0 is none; in Linux, non-zero enables sound + {"music_card",{&mus_card},{-1},-1,9, // to be set, -1 = autodetect + def_int,ss_none}, // select music driver (DOS), -1 is autodetect, 0 is none"; in Linux, non-zero enables music + {"pitched_sounds",{&pitched_sounds},{0},0,1, // killough 2/21/98 + def_bool,ss_none}, // enables variable pitch in sound effects (from id's original code) + {"samplerate",{&snd_samplerate},{44100},11025,48000, def_int,ss_none}, + {"slice_samplecount",{&snd_samplecount},{0},0,8192, def_int,ss_none}, + {"sfx_volume",{&snd_SfxVolume},{8},0,15, def_int,ss_none}, + {"music_volume",{&snd_MusicVolume},{8},0,15, def_int,ss_none}, + {"mus_pause_opt",{&mus_pause_opt},{1},0,2, // CPhipps - music pausing + def_int, ss_none}, // 0 = kill music when paused, 1 = pause music, 2 = let music continue + {"snd_channels",{&default_numChannels},{MAX_CHANNELS},1,MAX_CHANNELS, + def_int,ss_none}, // number of audio events simultaneously // killough +#ifdef _WIN32 + {"snd_midiplayer",{NULL, &snd_midiplayer},{0,"fluidsynth"},UL,UL,def_str,ss_none}, + {"snd_soundfont",{NULL, &snd_soundfont},{0,"TimGM6mb.sf2"},UL,UL,def_str,ss_none}, // soundfont name for synths that support it +#else + {"snd_midiplayer",{NULL, &snd_midiplayer},{0,"sdl"},UL,UL,def_str,ss_none}, + {"snd_soundfont",{NULL, &snd_soundfont},{0,"/usr/share/sounds/sf3/default-GM.sf3"},UL,UL,def_str,ss_none}, // soundfont name for synths that support it +#endif + {"snd_mididev",{NULL, &snd_mididev},{0,""},UL,UL,def_str,ss_none}, // midi device to use for portmidiplayer and alsaplayer + {"lowpass_filter",{&lowpass_filter},{0},0,1, + def_bool,ss_none}, // low-pass filter borrowed from Chocolate Doom so upscaling old audio doesn't sound too horrible + {"full_sounds",{&full_sounds},{0},0,1,def_bool,ss_none}, // disable sound cutoffs + + {"mus_fluidsynth_chorus",{&mus_fluidsynth_chorus},{0},0,1,def_bool,ss_none}, + {"mus_fluidsynth_reverb",{&mus_fluidsynth_reverb},{0},0,1,def_bool,ss_none}, + {"mus_fluidsynth_gain",{&mus_fluidsynth_gain},{50},0,1000,def_int,ss_none}, // NSM fine tune fluidsynth output level + {"mus_opl_gain",{&mus_opl_gain},{50},0,1000,def_int,ss_none}, // NSM fine tune opl output level + {"mus_portmidi_reset_type",{NULL, &mus_portmidi_reset_type},{0,"gm"},UL,UL,def_str,ss_none}, // portmidi reset type (none, gs, gm, gm2, xg) + {"mus_portmidi_reset_delay",{&mus_portmidi_reset_delay},{0},0,2000,def_int,ss_none}, // portmidi delay after reset (milliseconds) + {"mus_portmidi_filter_sysex",{&mus_portmidi_filter_sysex},{1},0,1,def_bool,ss_none}, // portmidi block sysex from midi files + {"mus_portmidi_reverb_level",{&mus_portmidi_reverb_level},{-1},-1,127,def_int,ss_none}, // portmidi reverb send level + {"mus_portmidi_chorus_level",{&mus_portmidi_chorus_level},{-1},-1,127,def_int,ss_none}, // portmidi chorus send level + + {"Video settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"videomode",{NULL, &default_videomode},{0,"8bit"},UL,UL,def_str,ss_none}, + /* 640x480 default resolution */ + {"screen_resolution",{NULL, &screen_resolution},{0,"640x480"},UL,UL,def_str,ss_none}, + {"use_fullscreen",{&use_fullscreen},{0},0,1, /* proff 21/05/2000 */ + def_bool,ss_none}, + {"exclusive_fullscreen",{&exclusive_fullscreen},{0},0,1, // [FG] mode-changing fullscreen + def_bool,ss_none}, + {"render_vsync",{&render_vsync},{1},0,1, + def_bool,ss_none}, + {"translucency",{&default_translucency},{1},0,1, // phares + def_bool,ss_none}, // enables translucency + {"tran_filter_pct",{&tran_filter_pct},{66},0,100, // killough 2/21/98 + def_int,ss_none}, // set percentage of foreground/background translucency mix + {"screenblocks",{&screenblocks},{10},3,11, // killough 2/21/98: default to 10 + def_int,ss_none}, + {"usegamma",{&usegamma},{0},0,4, //jff 3/6/98 fix erroneous upper limit in range + def_int,ss_none}, // gamma correction level // killough 1/18/98 + {"uncapped_framerate", {&movement_smooth_default}, {1},0,1, + def_bool,ss_stat}, + {"filter_wall",{(int*)&drawvars.filterwall},{RDRAW_FILTER_POINT}, + RDRAW_FILTER_POINT, RDRAW_FILTER_ROUNDED, def_int,ss_none}, + {"filter_floor",{(int*)&drawvars.filterfloor},{RDRAW_FILTER_POINT}, + RDRAW_FILTER_POINT, RDRAW_FILTER_ROUNDED, def_int,ss_none}, + {"filter_sprite",{(int*)&drawvars.filtersprite},{RDRAW_FILTER_POINT}, + RDRAW_FILTER_POINT, RDRAW_FILTER_ROUNDED, def_int,ss_none}, + {"filter_z",{(int*)&drawvars.filterz},{RDRAW_FILTER_POINT}, + RDRAW_FILTER_POINT, RDRAW_FILTER_LINEAR, def_int,ss_none}, + {"filter_patch",{(int*)&drawvars.filterpatch},{RDRAW_FILTER_POINT}, + RDRAW_FILTER_POINT, RDRAW_FILTER_ROUNDED, def_int,ss_none}, + {"filter_threshold",{(int*)&drawvars.mag_threshold},{49152}, + 0, UL, def_int,ss_none}, + {"sprite_edges",{(int*)&drawvars.sprite_edges},{RDRAW_MASKEDCOLUMNEDGE_SQUARE}, + RDRAW_MASKEDCOLUMNEDGE_SQUARE, RDRAW_MASKEDCOLUMNEDGE_SLOPED, def_int,ss_none}, + {"patch_edges",{(int*)&drawvars.patch_edges},{RDRAW_MASKEDCOLUMNEDGE_SQUARE}, + RDRAW_MASKEDCOLUMNEDGE_SQUARE, RDRAW_MASKEDCOLUMNEDGE_SLOPED, def_int,ss_none}, + + {"OpenGL settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"gl_compatibility", {&gl_compatibility}, {0},0,1, + def_bool,ss_stat}, + + {"gl_arb_multitexture", {&gl_arb_multitexture_default}, {1},0,1, + def_bool,ss_stat}, + {"gl_arb_texture_compression", {&gl_arb_texture_compression_default}, {1},0,1, + def_bool,ss_stat}, + {"gl_arb_texture_non_power_of_two", {&gl_arb_texture_non_power_of_two_default}, {1},0,1, + def_bool,ss_stat}, + {"gl_ext_arb_vertex_buffer_object", {&gl_ext_arb_vertex_buffer_object_default}, {1},0,1, + def_bool,ss_stat}, + {"gl_arb_pixel_buffer_object", {&gl_arb_pixel_buffer_object_default}, {1},0,1, + def_bool,ss_stat}, + {"gl_arb_shader_objects", {&gl_arb_shader_objects_default}, {1},0,1, + def_bool,ss_stat}, + {"gl_ext_blend_color", {&gl_ext_blend_color_default}, {1},0,1, + def_bool,ss_stat}, + {"gl_ext_framebuffer_object", {&gl_ext_framebuffer_object_default}, {1},0,1, + def_bool,ss_stat}, + {"gl_ext_packed_depth_stencil", {&gl_ext_packed_depth_stencil_default}, {1},0,1, + def_bool,ss_stat}, + {"gl_ext_texture_filter_anisotropic", {&gl_ext_texture_filter_anisotropic_default}, {1},0,1, + def_bool,ss_stat}, + {"gl_use_stencil", {&gl_use_stencil_default}, {1},0,1, + def_bool,ss_stat}, + {"gl_use_display_lists",{&gl_use_display_lists},{0},0,1, + def_bool,ss_none}, + + {"gl_finish",{&gl_finish},{1},0,1, + def_bool,ss_none}, + {"gl_clear",{&gl_clear},{0},0,1, + def_bool,ss_none}, + {"gl_ztrick",{&gl_ztrick},{0},0,1, + def_bool,ss_none}, + {"gl_nearclip",{&gl_nearclip},{5},0,UL, + def_int,ss_none}, /* near clipping plane pos */ + {"gl_colorbuffer_bits",{&gl_colorbuffer_bits},{32},16,32, + def_int,ss_none}, + {"gl_depthbuffer_bits",{&gl_depthbuffer_bits},{24},16,32, + def_int,ss_none}, + {"gl_texture_filter",{(int*)&gl_texture_filter}, + {filter_nearest_mipmap_linear}, filter_nearest, filter_count - 1, def_int,ss_none}, + {"gl_sprite_filter",{(int*)&gl_sprite_filter}, + {filter_nearest}, filter_nearest, filter_linear_mipmap_nearest, def_int,ss_none}, + {"gl_patch_filter",{(int*)&gl_patch_filter}, + {filter_nearest}, filter_nearest, filter_linear, def_int,ss_none}, + {"gl_texture_filter_anisotropic",{(int*)&gl_texture_filter_anisotropic}, + {gl_anisotropic_8x}, gl_anisotropic_off, gl_anisotropic_16x, def_int,ss_none}, + {"gl_tex_format_string", {NULL,&gl_tex_format_string}, {0,"GL_RGBA"},UL,UL, + def_str,ss_none}, + {"gl_sprite_offset",{&gl_sprite_offset_default},{0}, 0, 5, + def_int,ss_none}, // amount to bring items out of floor (GL) Mead 8/13/03 + {"gl_sprite_blend",{&gl_sprite_blend},{0},0,1, + def_bool,ss_none}, + {"gl_mask_sprite_threshold",{&gl_mask_sprite_threshold},{50},0,100, + def_int,ss_none}, + {"gl_skymode",{(int*)&gl_skymode}, + {skytype_auto}, skytype_auto, skytype_count - 1, def_int,ss_none}, + {"gl_sky_detail",{&gl_sky_detail},{16},1,32, + def_int,ss_none}, + {"gl_use_paletted_texture",{&gl_use_paletted_texture},{0},0,1, + def_bool,ss_none}, + {"gl_use_shared_texture_palette",{&gl_use_shared_texture_palette},{0},0,1, + def_bool,ss_none}, + + {"Mouse settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"use_mouse",{&usemouse},{1},0,1, + def_bool,ss_none}, // enables use of mouse with DOOM + //jff 4/3/98 allow unlimited sensitivity + {"mouse_sensitivity_horiz",{&mouseSensitivity_horiz},{10},0,UL, + def_int,ss_none}, /* adjust horizontal (x) mouse sensitivity killough/mead */ + //jff 4/3/98 allow unlimited sensitivity + {"mouse_sensitivity_vert",{&mouseSensitivity_vert},{1},0,UL, + def_int,ss_none}, /* adjust vertical (y) mouse sensitivity killough/mead */ + //jff 3/8/98 allow -1 in mouse bindings to disable mouse function + {"mouseb_fire",{&mousebfire},{0},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button number to use for fire + {"mouseb_strafe",{&mousebstrafe},{1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button number to use for strafing + {"mouseb_forward",{&mousebforward},{2},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button number to use for forward motion + {"mouseb_backward",{&mousebbackward},{-1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button number to use for backward motion + {"mouseb_turnright",{&mousebturnright},{-1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button number to use for turning right + {"mouseb_turnleft",{&mousebturnleft},{-1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button number to use for turning left + {"mouseb_use", {&mousebuse},{-1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button number to use for using doors/switches + {"mouseb_speed", {&mousebspeed},{-1},-1,MAX_MOUSEB, + def_int,ss_keys}, + //jff 3/8/98 end of lower range change for -1 allowed in mouse binding + + {"mb_weapon1",{&mb_weapon1},{-1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button to switch to weapon 1 (fist/chainsaw) + {"mb_weapon2",{&mb_weapon2},{-1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button to switch to weapon 2 (pistol) + {"mb_weapon3",{&mb_weapon3},{-1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button to switch to weapon 3 (supershotgun/shotgun) + {"mb_weapon4",{&mb_weapon4},{-1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button to switch to weapon 4 (chaingun) + {"mb_weapon5",{&mb_weapon5},{-1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button to switch to weapon 5 (rocket launcher) + {"mb_weapon6",{&mb_weapon6},{-1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button to switch to weapon 6 (plasma rifle) + {"mb_weapon7",{&mb_weapon7},{-1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button to switch to weapon 7 (bfg9000) + {"mb_weapon8",{&mb_weapon8},{-1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button to switch to weapon 8 (chainsaw) + {"mb_weapon9",{&mb_weapon9},{-1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button to switch to weapon 9 (supershotgun) + +// For key bindings, the values stored in the key_* variables // phares +// are the internal Doom Codes. The values stored in the default.cfg +// file are the keyboard codes. +// CPhipps - now they're the doom codes, so default.cfg can be portable + + {"Key bindings",{NULL},{0},UL,UL,def_none,ss_none}, + {"key_right", {&key_right}, {KEYD_RIGHTARROW}, + 0,MAX_KEY,def_key,ss_keys}, // key to turn right + {"key_left", {&key_left}, {KEYD_LEFTARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to turn left + {"key_up", {&key_up}, {'w'} , + 0,MAX_KEY,def_key,ss_keys}, // key to move forward + {"key_down", {&key_down}, {'s'}, + 0,MAX_KEY,def_key,ss_keys}, // key to move backward + {"key_mlook", {&key_mlook}, {'\\'}, + 0,MAX_KEY,def_key,ss_keys}, // key to move backward + {"key_novert", {&key_novert}, {0} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle novert mode + {"key_menu_right", {&key_menu_right}, {KEYD_RIGHTARROW},// phares 3/7/98 + 0,MAX_KEY,def_key,ss_keys}, // key to move right in a menu // | + {"key_menu_left", {&key_menu_left}, {KEYD_LEFTARROW} ,// V + 0,MAX_KEY,def_key,ss_keys}, // key to move left in a menu + {"key_menu_up", {&key_menu_up}, {KEYD_UPARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to move up in a menu + {"key_menu_down", {&key_menu_down}, {KEYD_DOWNARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to move down in a menu + {"key_menu_backspace",{&key_menu_backspace},{KEYD_BACKSPACE} , + 0,MAX_KEY,def_key,ss_keys}, // delete key in a menu + {"key_menu_escape", {&key_menu_escape}, {KEYD_ESCAPE} , + 0,MAX_KEY,def_key,ss_keys}, // key to leave a menu , // phares 3/7/98 + {"key_menu_enter", {&key_menu_enter}, {KEYD_ENTER} , + 0,MAX_KEY,def_key,ss_keys}, // key to select from menu + {"key_menu_clear", {&key_menu_clear}, {KEYD_DEL} , + 0,MAX_KEY,def_key,ss_keys}, // key to clear a key binding + {"key_setup", {&key_setup}, {0}, + 0,MAX_KEY,def_key,ss_keys}, //e6y: key for entering setup menu + {"key_strafeleft", {&key_strafeleft}, {'a'} , + 0,MAX_KEY,def_key,ss_keys}, // key to strafe left + {"key_straferight", {&key_straferight}, {'d'} , + 0,MAX_KEY,def_key,ss_keys}, // key to fly up + {"key_flyup", {&key_flyup}, {'.'}, + 0,MAX_KEY,def_key,ss_keys}, // key to fly down + {"key_flydown", {&key_flydown}, {','}, + 0,MAX_KEY,def_key,ss_keys}, // key to strafe right + + {"key_fire", {&key_fire}, {KEYD_RCTRL} , + 0,MAX_KEY,def_key,ss_keys}, // duh + {"key_use", {&key_use}, {' '} , + 0,MAX_KEY,def_key,ss_keys}, // key to open a door, use a switch + {"key_strafe", {&key_strafe}, {KEYD_RALT} , + 0,MAX_KEY,def_key,ss_keys}, // key to use with arrows to strafe + {"key_speed", {&key_speed}, {KEYD_RSHIFT} , + 0,MAX_KEY,def_key,ss_keys}, // key to run + + {"key_savegame", {&key_savegame}, {KEYD_F2} , + 0,MAX_KEY,def_key,ss_keys}, // key to save current game + {"key_loadgame", {&key_loadgame}, {KEYD_F3} , + 0,MAX_KEY,def_key,ss_keys}, // key to restore from saved games + {"key_soundvolume", {&key_soundvolume}, {KEYD_F4} , + 0,MAX_KEY,def_key,ss_keys}, // key to bring up sound controls + {"key_hud", {&key_hud}, {KEYD_F5} , + 0,MAX_KEY,def_key,ss_keys}, // key to adjust HUD + {"key_quicksave", {&key_quicksave}, {KEYD_F6} , + 0,MAX_KEY,def_key,ss_keys}, // key to to quicksave + {"key_endgame", {&key_endgame}, {KEYD_F7} , + 0,MAX_KEY,def_key,ss_keys}, // key to end the game + {"key_messages", {&key_messages}, {KEYD_F8} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle message enable + {"key_quickload", {&key_quickload}, {KEYD_F9} , + 0,MAX_KEY,def_key,ss_keys}, // key to load from quicksave + {"key_quit", {&key_quit}, {KEYD_F10} , + 0,MAX_KEY,def_key,ss_keys}, // key to quit game + {"key_gamma", {&key_gamma}, {KEYD_F11} , + 0,MAX_KEY,def_key,ss_keys}, // key to adjust gamma correction + {"key_spy", {&key_spy}, {KEYD_F12} , + 0,MAX_KEY,def_key,ss_keys}, // key to view from another coop player's view + {"key_pause", {&key_pause}, {KEYD_PAUSE} , + 0,MAX_KEY,def_key,ss_keys}, // key to pause the game + {"key_autorun", {&key_autorun}, {KEYD_CAPSLOCK} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle always run mode + {"key_chat", {&key_chat}, {'t'} , + 0,MAX_KEY,def_key,ss_keys}, // key to enter a chat message + {"key_backspace", {&key_backspace}, {KEYD_BACKSPACE} , + 0,MAX_KEY,def_key,ss_keys}, // backspace key + {"key_enter", {&key_enter}, {KEYD_ENTER} , + 0,MAX_KEY,def_key,ss_keys}, // key to select from menu or see last message + {"key_map", {&key_map}, {KEYD_TAB} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle automap display + {"key_map_right", {&key_map_right}, {KEYD_RIGHTARROW},// phares 3/7/98 + 0,MAX_KEY,def_key,ss_keys}, // key to shift automap right // | + {"key_map_left", {&key_map_left}, {KEYD_LEFTARROW} ,// V + 0,MAX_KEY,def_key,ss_keys}, // key to shift automap left + {"key_map_up", {&key_map_up}, {KEYD_UPARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to shift automap up + {"key_map_down", {&key_map_down}, {KEYD_DOWNARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to shift automap down + {"key_map_zoomin", {&key_map_zoomin}, {'='} , + 0,MAX_KEY,def_key,ss_keys}, // key to enlarge automap + {"key_map_zoomout", {&key_map_zoomout}, {'-'} , + 0,MAX_KEY,def_key,ss_keys}, // key to reduce automap + {"key_map_gobig", {&key_map_gobig}, {'0'} , + 0,MAX_KEY,def_key,ss_keys}, // key to get max zoom for automap + {"key_map_follow", {&key_map_follow}, {'f'} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle follow mode + {"key_map_mark", {&key_map_mark}, {'m'} , + 0,MAX_KEY,def_key,ss_keys}, // key to drop a marker on automap + {"key_map_clear", {&key_map_clear}, {'c'} , + 0,MAX_KEY,def_key,ss_keys}, // key to clear all markers on automap + {"key_map_grid", {&key_map_grid}, {'g'} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle grid display over automap + {"key_map_rotate", {&key_map_rotate}, {'r'} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle rotating the automap to match the player's orientation + {"key_map_overlay", {&key_map_overlay}, {'o'} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle overlaying the automap on the rendered display + {"key_map_textured", {&key_map_textured}, {0} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle textured automap + {"key_reverse", {&key_reverse}, {'/'} , + 0,MAX_KEY,def_key,ss_keys}, // key to spin 180 instantly + {"key_zoomin", {&key_zoomin}, {'='} , + 0,MAX_KEY,def_key,ss_keys}, // key to enlarge display + {"key_zoomout", {&key_zoomout}, {'-'} , + 0,MAX_KEY,def_key,ss_keys}, // key to reduce display + {"key_chatplayer1", {&destination_keys[0]}, {'g'} , + 0,MAX_KEY,def_key,ss_keys}, // key to chat with player 1 + // killough 11/98: fix 'i'/'b' reversal + {"key_chatplayer2", {&destination_keys[1]}, {'i'} , + 0,MAX_KEY,def_key,ss_keys}, // key to chat with player 2 + {"key_chatplayer3", {&destination_keys[2]}, {'b'} , + 0,MAX_KEY,def_key,ss_keys}, // key to chat with player 3 + {"key_chatplayer4", {&destination_keys[3]}, {'r'} , + 0,MAX_KEY,def_key,ss_keys}, // key to chat with player 4 + {"key_weapontoggle",{&key_weapontoggle}, {'0'} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle between two most preferred weapons with ammo + {"key_weapon1", {&key_weapon1}, {'1'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 1 (fist/chainsaw) + {"key_weapon2", {&key_weapon2}, {'2'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 2 (pistol) + {"key_weapon3", {&key_weapon3}, {'3'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 3 (supershotgun/shotgun) + {"key_weapon4", {&key_weapon4}, {'4'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 4 (chaingun) + {"key_weapon5", {&key_weapon5}, {'5'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 5 (rocket launcher) + {"key_weapon6", {&key_weapon6}, {'6'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 6 (plasma rifle) + {"key_weapon7", {&key_weapon7}, {'7'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 7 (bfg9000) // ^ + {"key_weapon8", {&key_weapon8}, {'8'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 8 (chainsaw) // | + {"key_weapon9", {&key_weapon9}, {'9'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 9 (supershotgun) // phares + {"key_nextweapon", {&key_nextweapon}, {KEYD_MWHEELUP} , + 0,MAX_KEY,def_key,ss_keys}, // key to cycle to the next weapon + {"key_prevweapon", {&key_prevweapon}, {KEYD_MWHEELDOWN}, + 0,MAX_KEY,def_key,ss_keys}, // key to cycle to the previous weapon + + // killough 2/22/98: screenshot key + {"key_screenshot", {&key_screenshot}, {'*'} , + 0,MAX_KEY,def_key,ss_keys}, // key to take a screenshot + + {"Joystick settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"use_joystick",{&usejoystick},{0},0,2, + def_int,ss_none}, // number of joystick to use (0 for none) + {"joy_left",{&joyleft},{0}, UL,UL,def_int,ss_none}, + {"joy_right",{&joyright},{0},UL,UL,def_int,ss_none}, + {"joy_up", {&joyup}, {0}, UL,UL,def_int,ss_none}, + {"joy_down",{&joydown},{0}, UL,UL,def_int,ss_none}, + {"joyb_fire",{&joybfire},{0},0,UL, + def_int,ss_keys}, // joystick button number to use for fire + {"joyb_strafe",{&joybstrafe},{1},0,UL, + def_int,ss_keys}, // joystick button number to use for strafing + {"joyb_strafeleft",{&joybstrafeleft},{4},0,UL, + def_int,ss_keys}, // joystick button number to use for strafe left + {"joyb_straferight",{&joybstraferight},{5},0,UL, + def_int,ss_keys}, // joystick button number to use for strafe right + {"joyb_speed",{&joybspeed},{2},0,UL, + def_int,ss_keys}, // joystick button number to use for running + {"joyb_use",{&joybuse},{3},0,UL, + def_int,ss_keys}, // joystick button number to use for use/open + + {"Chat macros",{NULL},{0},UL,UL,def_none,ss_none}, + {"chatmacro0", {0,&chat_macros[0]}, {0,HUSTR_CHATMACRO0},UL,UL, + def_str,ss_chat}, // chat string associated with 0 key + {"chatmacro1", {0,&chat_macros[1]}, {0,HUSTR_CHATMACRO1},UL,UL, + def_str,ss_chat}, // chat string associated with 1 key + {"chatmacro2", {0,&chat_macros[2]}, {0,HUSTR_CHATMACRO2},UL,UL, + def_str,ss_chat}, // chat string associated with 2 key + {"chatmacro3", {0,&chat_macros[3]}, {0,HUSTR_CHATMACRO3},UL,UL, + def_str,ss_chat}, // chat string associated with 3 key + {"chatmacro4", {0,&chat_macros[4]}, {0,HUSTR_CHATMACRO4},UL,UL, + def_str,ss_chat}, // chat string associated with 4 key + {"chatmacro5", {0,&chat_macros[5]}, {0,HUSTR_CHATMACRO5},UL,UL, + def_str,ss_chat}, // chat string associated with 5 key + {"chatmacro6", {0,&chat_macros[6]}, {0,HUSTR_CHATMACRO6},UL,UL, + def_str,ss_chat}, // chat string associated with 6 key + {"chatmacro7", {0,&chat_macros[7]}, {0,HUSTR_CHATMACRO7},UL,UL, + def_str,ss_chat}, // chat string associated with 7 key + {"chatmacro8", {0,&chat_macros[8]}, {0,HUSTR_CHATMACRO8},UL,UL, + def_str,ss_chat}, // chat string associated with 8 key + {"chatmacro9", {0,&chat_macros[9]}, {0,HUSTR_CHATMACRO9},UL,UL, + def_str,ss_chat}, // chat string associated with 9 key + + {"Automap settings",{NULL},{0},UL,UL,def_none,ss_none}, + //jff 1/7/98 defaults for automap colors + //jff 4/3/98 remove -1 in lower range, 0 now disables new map features + {"mapcolor_back", {&mapcolor_back}, {247},0,255, // black //jff 4/6/98 new black + def_colour,ss_auto}, // color used as background for automap + {"mapcolor_grid", {&mapcolor_grid}, {104},0,255, // dk gray + def_colour,ss_auto}, // color used for automap grid lines + {"mapcolor_wall", {&mapcolor_wall}, {23},0,255, // red-brown + def_colour,ss_auto}, // color used for one side walls on automap + {"mapcolor_fchg", {&mapcolor_fchg}, {55},0,255, // lt brown + def_colour,ss_auto}, // color used for lines floor height changes across + {"mapcolor_cchg", {&mapcolor_cchg}, {215},0,255, // orange + def_colour,ss_auto}, // color used for lines ceiling height changes across + {"mapcolor_clsd", {&mapcolor_clsd}, {208},0,255, // white + def_colour,ss_auto}, // color used for lines denoting closed doors, objects + {"mapcolor_rkey", {&mapcolor_rkey}, {175},0,255, // red + def_colour,ss_auto}, // color used for red key sprites + {"mapcolor_bkey", {&mapcolor_bkey}, {204},0,255, // blue + def_colour,ss_auto}, // color used for blue key sprites + {"mapcolor_ykey", {&mapcolor_ykey}, {231},0,255, // yellow + def_colour,ss_auto}, // color used for yellow key sprites + {"mapcolor_rdor", {&mapcolor_rdor}, {175},0,255, // red + def_colour,ss_auto}, // color used for closed red doors + {"mapcolor_bdor", {&mapcolor_bdor}, {204},0,255, // blue + def_colour,ss_auto}, // color used for closed blue doors + {"mapcolor_ydor", {&mapcolor_ydor}, {231},0,255, // yellow + def_colour,ss_auto}, // color used for closed yellow doors + {"mapcolor_tele", {&mapcolor_tele}, {119},0,255, // dk green + def_colour,ss_auto}, // color used for teleporter lines + {"mapcolor_secr", {&mapcolor_secr}, {252},0,255, // purple + def_colour,ss_auto}, // color used for lines around secret sectors + {"mapcolor_exit", {&mapcolor_exit}, {0},0,255, // none + def_colour,ss_auto}, // color used for exit lines + {"mapcolor_unsn", {&mapcolor_unsn}, {104},0,255, // dk gray + def_colour,ss_auto}, // color used for lines not seen without computer map + {"mapcolor_flat", {&mapcolor_flat}, {88},0,255, // lt gray + def_colour,ss_auto}, // color used for lines with no height changes + {"mapcolor_sprt", {&mapcolor_sprt}, {112},0,255, // green + def_colour,ss_auto}, // color used as things + {"mapcolor_item", {&mapcolor_item}, {231},0,255, // yellow + def_colour,ss_auto}, // color used for counted items + {"mapcolor_hair", {&mapcolor_hair}, {208},0,255, // white + def_colour,ss_auto}, // color used for dot crosshair denoting center of map + {"mapcolor_sngl", {&mapcolor_sngl}, {208},0,255, // white + def_colour,ss_auto}, // color used for the single player arrow + {"mapcolor_me", {&mapcolor_me}, {112},0,255, // green + def_colour,ss_auto}, // your (player) colour + {"mapcolor_enemy", {&mapcolor_enemy}, {177},0,255, + def_colour,ss_auto}, + {"mapcolor_frnd", {&mapcolor_frnd}, {112},0,255, + def_colour,ss_auto}, + //jff 3/9/98 add option to not show secrets til after found + {"map_secret_after", {&map_secret_after}, {0},0,1, // show secret after gotten + def_bool,ss_auto}, // prevents showing secret sectors till after entered + {"map_point_coord", {&map_point_coordinates}, {0},0,1, + def_bool,ss_auto}, + {"map_level_stat", {&map_level_stat}, {1},0,1, + def_bool,ss_auto}, + //jff 1/7/98 end additions for automap + {"automapmode", {(int*)&automapmode}, {am_follow}, 0, 31, // CPhipps - remember automap mode + def_hex,ss_none}, // automap mode + {"map_always_updates", {&map_always_updates}, {1},0,1, + def_bool,ss_auto}, + {"map_grid_size", {&map_grid_size}, {128},8,256, + def_int,ss_auto}, + {"map_scroll_speed", {&map_scroll_speed}, {8},1,32, + def_int,ss_auto}, + {"map_wheel_zoom", {&map_wheel_zoom}, {1},0,1, + def_bool,ss_auto}, + {"map_use_multisamling", {&map_use_multisamling}, {0},0,1, + def_bool,ss_auto}, + {"map_textured", {&map_textured}, {1},0,1, + def_bool,ss_auto}, + {"map_textured_trans", {&map_textured_trans}, {100},0,100, + def_int,ss_auto}, + {"map_textured_overlay_trans", {&map_textured_overlay_trans}, {66},0,100, + def_int,ss_auto}, + {"map_lines_overlay_trans", {&map_lines_overlay_trans}, {100},0,100, + def_int,ss_auto}, + {"map_overlay_pos_x", {&map_overlay_pos_x}, {0},0,319, + def_int,ss_auto}, + {"map_overlay_pos_y", {&map_overlay_pos_y}, {0},0,199, + def_int,ss_auto}, + {"map_overlay_pos_width", {&map_overlay_pos_width}, {320},0,320, + def_int,ss_auto}, + {"map_overlay_pos_height", {&map_overlay_pos_height}, {200},0,200, + def_int,ss_auto}, + {"map_things_appearance", {(int*)&map_things_appearance}, {map_things_appearance_max-1},0,map_things_appearance_max-1, + def_int,ss_auto}, + + {"Heads-up display settings",{NULL},{0},UL,UL,def_none,ss_none}, + //jff 2/16/98 defaults for color ranges in hud and status + {"hudcolor_titl", {&hudcolor_titl}, {5},0,9, // gold range + def_int,ss_auto}, // color range used for automap level title + {"hudcolor_xyco", {&hudcolor_xyco}, {3},0,9, // green range + def_int,ss_auto}, // color range used for automap coordinates + {"hudcolor_mapstat_title", {&hudcolor_mapstat_title}, {6},0,9, // red range + def_int,ss_auto}, // color range used for automap statistics for titles + {"hudcolor_mapstat_value", {&hudcolor_mapstat_value}, {2},0,9, // gray range + def_int,ss_auto}, // color range used for automap statistics for data + {"hudcolor_mapstat_time", {&hudcolor_mapstat_time}, {2},0,9, // gray range + def_int,ss_auto}, // color range used for automap statistics for level time and total time + {"hudcolor_mesg", {&hudcolor_mesg}, {6},0,9, // red range + def_int,ss_mess}, // color range used for messages during play + {"hudcolor_chat", {&hudcolor_chat}, {5},0,9, // gold range + def_int,ss_mess}, // color range used for chat messages and entry + {"hudcolor_list", {&hudcolor_list}, {5},0,9, // gold range //jff 2/26/98 + def_int,ss_mess}, // color range used for message review + {"hud_msg_lines", {&hud_msg_lines}, {1},1,16, // 1 line scrolling window + def_int,ss_mess}, // number of messages in review display (1=disable) + {"hud_list_bgon", {&hud_list_bgon}, {0},0,1, // solid window bg ena //jff 2/26/98 + def_bool,ss_mess}, // enables background window behind message review + + {"health_red", {&health_red} , {25},0,200, // below is red + def_int,ss_stat}, // amount of health for red to yellow transition + {"health_yellow", {&health_yellow}, {50},0,200, // below is yellow + def_int,ss_stat}, // amount of health for yellow to green transition + {"health_green", {&health_green} , {100},0,200,// below is green, above blue + def_int,ss_stat}, // amount of health for green to blue transition + {"armor_red", {&armor_red} , {25},0,200, // below is red + def_int,ss_stat}, // amount of armor for red to yellow transition + {"armor_yellow", {&armor_yellow} , {50},0,200, // below is yellow + def_int,ss_stat}, // amount of armor for yellow to green transition + {"armor_green", {&armor_green} , {100},0,200,// below is green, above blue + def_int,ss_stat}, // amount of armor for green to blue transition + {"ammo_red", {&ammo_red} , {25},0,100, // below 25% is red + def_int,ss_stat}, // percent of ammo for red to yellow transition + {"ammo_yellow", {&ammo_yellow} , {50},0,100, // below 50% is yellow, above green + def_int,ss_stat}, // percent of ammo for yellow to green transition + {"ammo_colour_behaviour",{(int*)&ammo_colour_behaviour}, + {ammo_colour_behaviour_max-1}, // whether backpack changes thresholds above + 0,ammo_colour_behaviour_max-1,def_int,ss_stat}, + + //jff 2/16/98 HUD and status feature controls + {"hud_num", {&hud_num}, {6},0,100, + def_int,ss_none}, + //jff 2/23/98 + {"hud_displayed", {&hud_displayed}, {0},0,1, // whether hud is displayed + def_bool,ss_none}, // enables display of HUD + +//e6y + {"Prboom-plus key bindings",{NULL},{0},UL,UL,def_none,ss_none}, + {"key_speedup", {&key_speed_up}, {0}, + 0,MAX_KEY,def_key,ss_keys}, + {"key_speeddown", {&key_speed_down}, {0}, + 0,MAX_KEY,def_key,ss_keys}, + {"key_speeddefault", {&key_speed_default}, {0}, + 0,MAX_KEY,def_key,ss_keys}, + {"speed_step",{&speed_step},{0},0,1000, + def_int,ss_none}, + {"key_demo_skip", {&key_demo_skip}, {KEYD_INSERT}, + 0,MAX_KEY,def_key,ss_keys}, + {"key_level_restart", {&key_level_restart}, {KEYD_HOME}, + 0,MAX_KEY,def_key,ss_keys}, + {"key_nextlevel", {&key_nextlevel}, {KEYD_PAGEDOWN}, + 0,MAX_KEY,def_key,ss_keys}, + {"key_demo_jointogame", {&key_demo_jointogame}, {'q'}, + 0,MAX_KEY,def_key,ss_keys}, + {"key_demo_endlevel", {&key_demo_endlevel}, {KEYD_END}, + 0,MAX_KEY,def_key,ss_keys}, + {"key_walkcamera", {&key_walkcamera}, {KEYD_KEYPAD0}, + 0,MAX_KEY,def_key,ss_keys}, + {"key_showalive", {&key_showalive}, {KEYD_KEYPADDIVIDE}, + 0,MAX_KEY,def_key,ss_keys}, + + {"Prboom-plus heads-up display settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"hudadd_gamespeed", {&hudadd_gamespeed}, {0},0,1, + def_bool,ss_stat}, + {"hudadd_leveltime", {&hudadd_leveltime}, {0},0,1, + def_bool,ss_stat}, + {"hudadd_demotime", {&hudadd_demotime}, {0},0,1, + def_bool,ss_stat}, + {"hudadd_secretarea", {&hudadd_secretarea}, {0},0,1, + def_bool,ss_stat}, + {"hudadd_smarttotals", {&hudadd_smarttotals}, {0},0,1, + def_bool,ss_stat}, + {"hudadd_demoprogressbar", {&hudadd_demoprogressbar}, {1},0,1, + def_bool,ss_stat}, + {"hudadd_timests", {&hudadd_timests}, {0},0,1, + def_bool,ss_stat}, + {"hudadd_crosshair", {&hudadd_crosshair}, {0},0,HU_CROSSHAIRS-1, + def_bool,ss_stat}, + {"hudadd_crosshair_scale", {&hudadd_crosshair_scale}, {0},0,1, + def_bool,ss_stat}, + {"hudadd_crosshair_color", {&hudadd_crosshair_color}, {3},0,9, + def_int,ss_stat}, + {"hudadd_crosshair_health", {&hudadd_crosshair_health}, {0},0,1, + def_bool,ss_stat}, + {"hudadd_crosshair_target", {&hudadd_crosshair_target}, {0},0,1, + def_bool,ss_stat}, + {"hudadd_crosshair_target_color", {&hudadd_crosshair_target_color}, {9},0,9, + def_int,ss_stat}, + {"hudadd_crosshair_lock_target", {&hudadd_crosshair_lock_target}, {0},0,1, + def_bool,ss_stat}, + + //e6y + {"Prboom-plus mouse settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"mouse_acceleration",{&mouse_acceleration},{0},0,UL, + def_int,ss_none}, + {"mouse_sensitivity_mlook",{&mouseSensitivity_mlook},{10},0,UL, + def_int,ss_none}, + {"mouse_doubleclick_as_use", {&mouse_doubleclick_as_use}, {1},0,1, + def_bool,ss_stat}, + {"mouse_carrytics", {&mouse_carrytics}, {0},0,1, + def_bool,ss_stat}, + + {"Prboom-plus demos settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"demo_extendedformat", {&demo_extendedformat_default}, {1},0,1, + def_bool,ss_stat}, + {"demo_demoex_filename", {NULL,&demo_demoex_filename}, {0,""},UL,UL, + def_str,ss_none}, + {"getwad_cmdline", {NULL, &getwad_cmdline}, {0,""},UL,UL, + def_str,ss_none}, + {"demo_overwriteexisting", {&demo_overwriteexisting}, {1},0,1, + def_bool,ss_stat}, + {"quickstart_window_ms", {&quickstart_window_ms}, {0},0,1000, + def_int,ss_stat}, + + {"Prboom-plus game settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"movement_strafe50", {&movement_strafe50}, {0},0,1, + def_bool,ss_stat}, + {"movement_shorttics", {&movement_shorttics}, {0},0,1, + def_bool,ss_stat}, + {"interpolation_maxobjects", {&interpolation_maxobjects}, {0},0,UL, + def_int,ss_stat}, + + {"Prboom-plus misc settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"showendoom", {&showendoom}, {0},0,1, + def_bool,ss_stat}, + {"screenshot_dir", {NULL,&screenshot_dir}, {0,""},UL,UL, + def_str,ss_none}, + {"health_bar", {&health_bar}, {0},0,1, + def_bool,ss_stat}, + {"health_bar_full_length", {&health_bar_full_length}, {1},0,1, + def_bool,ss_stat}, + {"health_bar_red", {&health_bar_red}, {50},0,100, + def_int,ss_stat}, + {"health_bar_yellow", {&health_bar_yellow}, {99},0,100, + def_int,ss_stat}, + {"health_bar_green", {&health_bar_green}, {0},0,100, + def_int,ss_stat}, + + // NSM + {"Video capture encoding settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"cap_soundcommand",{NULL, &cap_soundcommand},{0,"ffmpeg -f s16le -ar %s -ac 2 -i - -c:a libopus -y temp_a.nut"},UL,UL,def_str,ss_none}, + {"cap_videocommand",{NULL, &cap_videocommand},{0,"ffmpeg -f rawvideo -pix_fmt rgb24 -r %r -s %wx%h -i - -c:v libx264 -y temp_v.nut"},UL,UL,def_str,ss_none}, + {"cap_muxcommand",{NULL, &cap_muxcommand},{0,"ffmpeg -i temp_v.nut -i temp_a.nut -c copy -y %f"},UL,UL,def_str,ss_none}, + {"cap_tempfile1",{NULL, &cap_tempfile1},{0,"temp_a.nut"},UL,UL,def_str,ss_none}, + {"cap_tempfile2",{NULL, &cap_tempfile2},{0,"temp_v.nut"},UL,UL,def_str,ss_none}, + {"cap_remove_tempfiles", {&cap_remove_tempfiles},{1},0,1,def_bool,ss_none}, + {"cap_fps", {&cap_fps},{60},16,300,def_int,ss_none}, + {"cap_wipescreen", {&cap_wipescreen},{0},0,1,def_bool,ss_none}, + + {"Prboom-plus video settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"sdl_video_window_pos", {NULL,&sdl_video_window_pos}, {0,"center"},UL,UL, + def_str,ss_none}, + {"palette_ondamage", {&palette_ondamage}, {1},0,1, + def_bool,ss_stat}, + {"palette_onbonus", {&palette_onbonus}, {1},0,1, + def_bool,ss_stat}, + {"palette_onpowers", {&palette_onpowers}, {1},0,1, + def_bool,ss_stat}, + {"render_wipescreen", {&render_wipescreen}, {1},0,1, + def_bool,ss_stat}, + {"render_screen_multiply", {&render_screen_multiply}, {1},1,5, + def_int,ss_stat}, + {"integer_scaling", {&integer_scaling}, {0},0,1, + def_bool,ss_stat}, + {"render_aspect", {&render_aspect}, {0},0,4, + def_int,ss_stat}, + {"render_doom_lightmaps", {&render_doom_lightmaps}, {0},0,1, + def_bool,ss_stat}, + {"fake_contrast", {&fake_contrast}, {1},0,1, + def_bool,ss_stat}, /* cph - allow crappy fake contrast to be disabled */ + {"render_stretch_hud", {&render_stretch_hud_default},{patch_stretch_16x10},0,patch_stretch_max - 1, + def_int,ss_stat}, + {"render_patches_scalex", {&render_patches_scalex},{0},0,16, + def_int,ss_stat}, + {"render_patches_scaley", {&render_patches_scaley},{0},0,16, + def_int,ss_stat}, + {"render_stretchsky",{&r_stretchsky},{1},0,1, + def_bool,ss_none}, + {"sprites_doom_order", {&sprites_doom_order}, {DOOM_ORDER_STATIC},0,DOOM_ORDER_LAST - 1, + def_int,ss_stat}, + + {"movement_mouselook", {&movement_mouselook}, {0},0,1, + def_bool,ss_stat}, + {"movement_mousenovert", {&movement_mousenovert}, {0},0,1, + def_bool,ss_stat}, + {"movement_maxviewpitch", {&movement_maxviewpitch}, {90},0,90, + def_int,ss_stat}, + {"movement_mousestrafedivisor", {&movement_mousestrafedivisor}, {4},1,512, + def_int,ss_stat}, + {"movement_mouseinvert", {&movement_mouseinvert}, {0},0,1, + def_bool,ss_stat}, + + {"Prboom-plus OpenGL settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"gl_allow_detail_textures", {&gl_allow_detail_textures}, {1},0,1, + def_bool,ss_stat}, + {"gl_detail_maxdist", {&gl_detail_maxdist}, {0},0,65535, + def_int,ss_stat}, + {"render_multisampling", {&render_multisampling}, {0},0,8, + def_int,ss_stat}, + {"render_fov", {&render_fov}, {90},20,160, + def_int,ss_stat}, + {"gl_spriteclip",{(int*)&gl_spriteclip},{spriteclip_smart}, spriteclip_const, spriteclip_smart, def_int,ss_none}, + {"gl_spriteclip_threshold", {&gl_spriteclip_threshold}, {10},0,100, + def_int,ss_stat}, + {"gl_sprites_frustum_culling", {&gl_sprites_frustum_culling}, {1},0,1, + def_bool,ss_stat}, + {"render_paperitems", {&render_paperitems}, {0},0,1, + def_bool,ss_stat}, + {"gl_boom_colormaps", {&gl_boom_colormaps_default}, {1},0,1, + def_bool,ss_stat}, + {"gl_hires_24bit_colormap", {&gl_hires_24bit_colormap}, {0},0,1, + def_bool,ss_stat}, + {"gl_texture_internal_hires", {&gl_texture_internal_hires}, {1},0,1, + def_bool,ss_stat}, + {"gl_texture_external_hires", {&gl_texture_external_hires}, {0},0,1, + def_bool,ss_stat}, + {"gl_hires_override_pwads", {&gl_hires_override_pwads}, {0},0,1, + def_bool,ss_stat}, + {"gl_texture_hires_dir", {NULL,&gl_texture_hires_dir}, {0,""},UL,UL, + def_str,ss_none}, + {"gl_texture_hqresize", {&gl_texture_hqresize}, {0},0,1, + def_bool,ss_stat}, + {"gl_texture_hqresize_textures", {&gl_texture_hqresize_textures}, + {hq_scale_2x},hq_scale_none,hq_scale_max-1, def_int,ss_stat}, + {"gl_texture_hqresize_sprites", {&gl_texture_hqresize_sprites}, + {hq_scale_none},hq_scale_none,hq_scale_max-1, def_int,ss_stat}, + {"gl_texture_hqresize_patches", {&gl_texture_hqresize_patches}, + {hq_scale_2x},hq_scale_none,hq_scale_max-1,def_int,ss_stat}, + {"gl_motionblur", {&gl_motionblur}, {0},0,1, + def_bool,ss_stat}, + {"gl_motionblur_min_speed", {NULL,&motion_blur.str_min_speed}, {0,"21.36"},UL,UL, + def_str,ss_none}, + {"gl_motionblur_min_angle", {NULL,&motion_blur.str_min_angle}, {0,"20.0"},UL,UL, + def_str,ss_none}, + {"gl_motionblur_att_a", {NULL,&motion_blur.str_att_a}, {0,"55.0"},UL,UL, + def_str,ss_none}, + {"gl_motionblur_att_b", {NULL,&motion_blur.str_att_b}, {0,"1.8"},UL,UL, + def_str,ss_none}, + {"gl_motionblur_att_c", {NULL,&motion_blur.str_att_c}, {0,"0.9"},UL,UL, + def_str,ss_none}, + {"gl_lightmode",{(int*)&gl_lightmode_default},{gl_lightmode_glboom}, + gl_lightmode_glboom, gl_lightmode_last-1, def_int,ss_none}, + {"gl_light_ambient", {&gl_light_ambient}, {20},1,255, + def_int,ss_stat}, + {"gl_fog", {&gl_fog}, {1},0,1, + def_bool,ss_stat}, + {"gl_fog_color", {&gl_fog_color}, {0},0,0xffffff, + def_hex,ss_stat}, + {"useglgamma",{&useglgamma},{6},0,MAX_GLGAMMA, + def_int,ss_none}, + {"gl_color_mip_levels", {&gl_color_mip_levels}, {0},0,1, + def_bool,ss_stat}, + {"gl_shadows", {&simple_shadows.enable}, {0},0,1, + def_bool,ss_stat}, + {"gl_shadows_maxdist",{&gl_shadows_maxdist},{1000},0,32767, + def_int,ss_none}, + {"gl_shadows_factor",{&gl_shadows_factor},{128},0,255, + def_int,ss_none}, + {"gl_blend_animations",{&gl_blend_animations},{0},0,1, + def_bool,ss_none}, + {"gl_thingspritefuzzmode",{(int*)&gl_thingspritefuzzmode},{fuzz_darken},fuzz_darken,fuzz_last-1, + def_int, ss_none}, + {"gl_weaponspritefuzzmode",{(int*)&gl_weaponspritefuzzmode},{fuzz_darken},fuzz_darken,fuzz_last-1, + def_int, ss_none}, + + {"Prboom-plus emulation settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"overrun_spechit_warn", {&overflows[OVERFLOW_SPECHIT].warn}, {0},0,1, + def_bool,ss_stat}, + {"overrun_spechit_emulate", {&overflows[OVERFLOW_SPECHIT].emulate}, {1},0,1, + def_bool,ss_stat}, + {"overrun_reject_warn", {&overflows[OVERFLOW_REJECT].warn}, {0},0,1, + def_bool,ss_stat}, + {"overrun_reject_emulate", {&overflows[OVERFLOW_REJECT].emulate}, {1},0,1, + def_bool,ss_stat}, + {"overrun_intercept_warn", {&overflows[OVERFLOW_INTERCEPT].warn}, {0},0,1, + def_bool,ss_stat}, + {"overrun_intercept_emulate", {&overflows[OVERFLOW_INTERCEPT].emulate}, {1},0,1, + def_bool,ss_stat}, + {"overrun_playeringame_warn", {&overflows[OVERFLOW_PLYERINGAME].warn}, {0},0,1, + def_bool,ss_stat}, + {"overrun_playeringame_emulate", {&overflows[OVERFLOW_PLYERINGAME].emulate}, {1},0,1, + def_bool,ss_stat}, + {"overrun_donut_warn", {&overflows[OVERFLOW_DONUT].warn}, {0},0,1, + def_bool,ss_stat}, + {"overrun_donut_emulate", {&overflows[OVERFLOW_DONUT].emulate}, {0},0,1, + def_bool,ss_stat}, + {"overrun_missedbackside_warn", {&overflows[OVERFLOW_MISSEDBACKSIDE].warn}, {0},0,1, + def_bool,ss_stat}, + {"overrun_missedbackside_emulate", {&overflows[OVERFLOW_MISSEDBACKSIDE].emulate}, {0},0,1, + def_bool,ss_stat}, + + {"Prboom-plus 'bad' compatibility settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"comperr_zerotag", {&default_comperr[comperr_zerotag]}, {0},0,1, + def_bool,ss_stat}, + {"comperr_passuse", {&default_comperr[comperr_passuse]}, {0},0,1, + def_bool,ss_stat}, + {"comperr_hangsolid", {&default_comperr[comperr_hangsolid]}, {0},0,1, + def_bool,ss_stat}, + {"comperr_blockmap", {&default_comperr[comperr_blockmap]}, {0},0,1, + def_bool,ss_stat}, + {"comperr_allowjump", {&default_comperr[comperr_allowjump]}, {0},0,2, + def_int,ss_stat}, + {"comperr_freeaim", {&default_comperr[comperr_freeaim]}, {0},0,1, + def_bool,ss_stat}, + +#ifdef USE_WINDOWS_LAUNCHER + {"Prboom-plus launcher settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"launcher_enable",{(int*)&launcher_enable},{launcher_enable_never}, + launcher_enable_never, launcher_enable_count - 1, def_int,ss_none}, + {"launcher_history0", {NULL,&launcher_history[0]}, {0,""},UL,UL,def_str,ss_none}, + {"launcher_history1", {NULL,&launcher_history[1]}, {0,""},UL,UL,def_str,ss_none}, + {"launcher_history2", {NULL,&launcher_history[2]}, {0,""},UL,UL,def_str,ss_none}, + {"launcher_history3", {NULL,&launcher_history[3]}, {0,""},UL,UL,def_str,ss_none}, + {"launcher_history4", {NULL,&launcher_history[4]}, {0,""},UL,UL,def_str,ss_none}, + {"launcher_history5", {NULL,&launcher_history[5]}, {0,""},UL,UL,def_str,ss_none}, + {"launcher_history6", {NULL,&launcher_history[6]}, {0,""},UL,UL,def_str,ss_none}, + {"launcher_history7", {NULL,&launcher_history[7]}, {0,""},UL,UL,def_str,ss_none}, + {"launcher_history8", {NULL,&launcher_history[8]}, {0,""},UL,UL,def_str,ss_none}, + {"launcher_history9", {NULL,&launcher_history[9]}, {0,""},UL,UL,def_str,ss_none}, +#endif + {"Prboom-plus demo patterns list. Put your patterns here",{NULL},{0},UL,UL,def_none,ss_none}, + {"demo_patterns_mask", {NULL, &demo_patterns_mask, &demo_patterns_count, &demo_patterns_list}, {0,"demo_pattern",9, &demo_patterns_list_def[0]},UL,UL,def_arr,ss_none}, + {"demo_pattern0", {NULL,&demo_patterns_list_def[0]}, + {0,"DOOM 2: Hell on Earth/((lv)|(nm)|(pa)|(ty))\\d\\d.\\d\\d\\d\\.lmp/doom2.wad"},UL,UL,def_str,ss_none}, + {"demo_pattern1", {NULL,&demo_patterns_list_def[1]}, + {0,"DOOM 2: Plutonia Experiment/p(c|f|l|n|p|r|s|t)\\d\\d.\\d\\d\\d\\.lmp/doom2.wad|plutonia.wad"},UL,UL,def_str,ss_none}, + {"demo_pattern2", {NULL,&demo_patterns_list_def[2]}, + {0,"DOOM 2: TNT - Evilution/((e(c|f|v|p|r|s|t))|(tn))\\d\\d.\\d\\d\\d\\.lmp/doom2.wad|tnt.wad"},UL,UL,def_str,ss_none}, + {"demo_pattern3", {NULL,&demo_patterns_list_def[3]}, + {0,"The Ultimate DOOM/(((e|f|n|p|r|t|u)\\dm\\d)|(n\\ds\\d)).\\d\\d\\d\\.lmp/doom.wad"},UL,UL,def_str,ss_none}, + {"demo_pattern4", {NULL,&demo_patterns_list_def[4]}, + {0,"Alien Vendetta/a(c|f|n|p|r|s|t|v)\\d\\d.\\d\\d\\d\\.lmp/doom2.wad|av.wad|av.deh"},UL,UL,def_str,ss_none}, + {"demo_pattern5", {NULL,&demo_patterns_list_def[5]}, + {0,"Requiem/r(c|f|n|p|q|r|s|t)\\d\\d.\\d\\d\\d\\.lmp/doom2.wad|requiem.wad|req21fix.wad|reqmus.wad"},UL,UL,def_str,ss_none}, + {"demo_pattern6", {NULL,&demo_patterns_list_def[6]}, + {0,"Hell Revealed/h(c|e|f|n|p|r|s|t)\\d\\d.\\d\\d\\d\\.lmp/doom2.wad|hr.wad|hrmus.wad"},UL,UL,def_str,ss_none}, + {"demo_pattern7", {NULL,&demo_patterns_list_def[7]}, + {0,"Memento Mori/mm\\d\\d.\\d\\d\\d\\.lmp/doom2.wad|mm.wad|mmmus.wad"},UL,UL,def_str,ss_none}, + {"demo_pattern8", {NULL,&demo_patterns_list_def[8]}, + {0,"Memento Mori 2/m2\\d\\d.\\d\\d\\d\\.lmp/doom2.wad|mm2.wad|mm2mus.wad"},UL,UL,def_str,ss_none}, + + {"Weapon preferences",{NULL},{0},UL,UL,def_none,ss_none}, + // killough 2/8/98: weapon preferences set by user: + {"weapon_choice_1", {&weapon_preferences[0][0]}, {6}, 0,9, + def_int,ss_weap}, // first choice for weapon (best) + {"weapon_choice_2", {&weapon_preferences[0][1]}, {9}, 0,9, + def_int,ss_weap}, // second choice for weapon + {"weapon_choice_3", {&weapon_preferences[0][2]}, {4}, 0,9, + def_int,ss_weap}, // third choice for weapon + {"weapon_choice_4", {&weapon_preferences[0][3]}, {3}, 0,9, + def_int,ss_weap}, // fourth choice for weapon + {"weapon_choice_5", {&weapon_preferences[0][4]}, {2}, 0,9, + def_int,ss_weap}, // fifth choice for weapon + {"weapon_choice_6", {&weapon_preferences[0][5]}, {8}, 0,9, + def_int,ss_weap}, // sixth choice for weapon + {"weapon_choice_7", {&weapon_preferences[0][6]}, {5}, 0,9, + def_int,ss_weap}, // seventh choice for weapon + {"weapon_choice_8", {&weapon_preferences[0][7]}, {7}, 0,9, + def_int,ss_weap}, // eighth choice for weapon + {"weapon_choice_9", {&weapon_preferences[0][8]}, {1}, 0,9, + def_int,ss_weap}, // ninth choice for weapon (worst) + + // cournia - support for arbitrary music file (defaults are mp3) + {"Music", {NULL},{0},UL,UL,def_none,ss_none}, + {"mus_e1m1", {0,&S_music_files[mus_e1m1]}, {0,"e1m1.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m2", {0,&S_music_files[mus_e1m2]}, {0,"e1m2.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m3", {0,&S_music_files[mus_e1m3]}, {0,"e1m3.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m4", {0,&S_music_files[mus_e1m4]}, {0,"e1m4.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m5", {0,&S_music_files[mus_e1m5]}, {0,"e1m5.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m6", {0,&S_music_files[mus_e1m6]}, {0,"e1m6.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m7", {0,&S_music_files[mus_e1m7]}, {0,"e1m7.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m8", {0,&S_music_files[mus_e1m8]}, {0,"e1m8.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m9", {0,&S_music_files[mus_e1m9]}, {0,"e1m9.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m1", {0,&S_music_files[mus_e2m1]}, {0,"e2m1.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m2", {0,&S_music_files[mus_e2m2]}, {0,"e2m2.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m3", {0,&S_music_files[mus_e2m3]}, {0,"e2m3.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m4", {0,&S_music_files[mus_e2m4]}, {0,"e2m4.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m5", {0,&S_music_files[mus_e2m5]}, {0,"e1m7.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m6", {0,&S_music_files[mus_e2m6]}, {0,"e2m6.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m7", {0,&S_music_files[mus_e2m7]}, {0,"e2m7.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m8", {0,&S_music_files[mus_e2m8]}, {0,"e2m8.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m9", {0,&S_music_files[mus_e2m9]}, {0,"e3m1.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m1", {0,&S_music_files[mus_e3m1]}, {0,"e3m1.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m2", {0,&S_music_files[mus_e3m2]}, {0,"e3m2.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m3", {0,&S_music_files[mus_e3m3]}, {0,"e3m3.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m4", {0,&S_music_files[mus_e3m4]}, {0,"e1m8.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m5", {0,&S_music_files[mus_e3m5]}, {0,"e1m7.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m6", {0,&S_music_files[mus_e3m6]}, {0,"e1m6.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m7", {0,&S_music_files[mus_e3m7]}, {0,"e2m7.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m8", {0,&S_music_files[mus_e3m8]}, {0,"e3m8.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m9", {0,&S_music_files[mus_e3m9]}, {0,"e1m9.mp3"},UL,UL, + def_str,ss_none}, + {"mus_inter", {0,&S_music_files[mus_inter]}, {0,"e2m3.mp3"},UL,UL, + def_str,ss_none}, + {"mus_intro", {0,&S_music_files[mus_intro]}, {0,"intro.mp3"},UL,UL, + def_str,ss_none}, + {"mus_bunny", {0,&S_music_files[mus_bunny]}, {0,"bunny.mp3"},UL,UL, + def_str,ss_none}, + {"mus_victor", {0,&S_music_files[mus_victor]}, {0,"victor.mp3"},UL,UL, + def_str,ss_none}, + {"mus_introa", {0,&S_music_files[mus_introa]}, {0,"intro.mp3"},UL,UL, + def_str,ss_none}, + {"mus_runnin", {0,&S_music_files[mus_runnin]}, {0,"runnin.mp3"},UL,UL, + def_str,ss_none}, + {"mus_stalks", {0,&S_music_files[mus_stalks]}, {0,"stalks.mp3"},UL,UL, + def_str,ss_none}, + {"mus_countd", {0,&S_music_files[mus_countd]}, {0,"countd.mp3"},UL,UL, + def_str,ss_none}, + {"mus_betwee", {0,&S_music_files[mus_betwee]}, {0,"betwee.mp3"},UL,UL, + def_str,ss_none}, + {"mus_doom", {0,&S_music_files[mus_doom]}, {0,"doom.mp3"},UL,UL, + def_str,ss_none}, + {"mus_the_da", {0,&S_music_files[mus_the_da]}, {0,"the_da.mp3"},UL,UL, + def_str,ss_none}, + {"mus_shawn", {0,&S_music_files[mus_shawn]}, {0,"shawn.mp3"},UL,UL, + def_str,ss_none}, + {"mus_ddtblu", {0,&S_music_files[mus_ddtblu]}, {0,"ddtblu.mp3"},UL,UL, + def_str,ss_none}, + {"mus_in_cit", {0,&S_music_files[mus_in_cit]}, {0,"in_cit.mp3"},UL,UL, + def_str,ss_none}, + {"mus_dead", {0,&S_music_files[mus_dead]}, {0,"dead.mp3"},UL,UL, + def_str,ss_none}, + {"mus_stlks2", {0,&S_music_files[mus_stlks2]}, {0,"stalks.mp3"},UL,UL, + def_str,ss_none}, + {"mus_theda2", {0,&S_music_files[mus_theda2]}, {0,"the_da.mp3"},UL,UL, + def_str,ss_none}, + {"mus_doom2", {0,&S_music_files[mus_doom2]}, {0,"doom.mp3"},UL,UL, + def_str,ss_none}, + {"mus_ddtbl2", {0,&S_music_files[mus_ddtbl2]}, {0,"ddtblu.mp3"},UL,UL, + def_str,ss_none}, + {"mus_runni2", {0,&S_music_files[mus_runni2]}, {0,"runnin.mp3"},UL,UL, + def_str,ss_none}, + {"mus_dead2", {0,&S_music_files[mus_dead2]}, {0,"dead.mp3"},UL,UL, + def_str,ss_none}, + {"mus_stlks3", {0,&S_music_files[mus_stlks3]}, {0,"stalks.mp3"},UL,UL, + def_str,ss_none}, + {"mus_romero", {0,&S_music_files[mus_romero]}, {0,"romero.mp3"},UL,UL, + def_str,ss_none}, + {"mus_shawn2", {0,&S_music_files[mus_shawn2]}, {0,"shawn.mp3"},UL,UL, + def_str,ss_none}, + {"mus_messag", {0,&S_music_files[mus_messag]}, {0,"messag.mp3"},UL,UL, + def_str,ss_none}, + {"mus_count2", {0,&S_music_files[mus_count2]}, {0,"countd.mp3"},UL,UL, + def_str,ss_none}, + {"mus_ddtbl3", {0,&S_music_files[mus_ddtbl3]}, {0,"ddtblu.mp3"},UL,UL, + def_str,ss_none}, + {"mus_ampie", {0,&S_music_files[mus_ampie]}, {0,"ampie.mp3"},UL,UL, + def_str,ss_none}, + {"mus_theda3", {0,&S_music_files[mus_theda3]}, {0,"the_da.mp3"},UL,UL, + def_str,ss_none}, + {"mus_adrian", {0,&S_music_files[mus_adrian]}, {0,"adrian.mp3"},UL,UL, + def_str,ss_none}, + {"mus_messg2", {0,&S_music_files[mus_messg2]}, {0,"messag.mp3"},UL,UL, + def_str,ss_none}, + {"mus_romer2", {0,&S_music_files[mus_romer2]}, {0,"romero.mp3"},UL,UL, + def_str,ss_none}, + {"mus_tense", {0,&S_music_files[mus_tense]}, {0,"tense.mp3"},UL,UL, + def_str,ss_none}, + {"mus_shawn3", {0,&S_music_files[mus_shawn3]}, {0,"shawn.mp3"},UL,UL, + def_str,ss_none}, + {"mus_openin", {0,&S_music_files[mus_openin]}, {0,"openin.mp3"},UL,UL, + def_str,ss_none}, + {"mus_evil", {0,&S_music_files[mus_evil]}, {0,"evil.mp3"},UL,UL, + def_str,ss_none}, + {"mus_ultima", {0,&S_music_files[mus_ultima]}, {0,"ultima.mp3"},UL,UL, + def_str,ss_none}, + {"mus_read_m", {0,&S_music_files[mus_read_m]}, {0,"read_m.mp3"},UL,UL, + def_str,ss_none}, + {"mus_dm2ttl", {0,&S_music_files[mus_dm2ttl]}, {0,"dm2ttl.mp3"},UL,UL, + def_str,ss_none}, + {"mus_dm2int", {0,&S_music_files[mus_dm2int]}, {0,"dm2int.mp3"},UL,UL, + def_str,ss_none}, +}; + +int numdefaults; +static char* defaultfile; // CPhipps - static, const + +// +// M_SaveDefaults +// + +void M_SaveDefaults (void) +{ + int i; + FILE* f; + int maxlen = 0; + + f = M_fopen (defaultfile, "w"); + if (!f) + return; // can't write the file, but don't complain + + // get maximum config key string length + for (i = 0 ; i < numdefaults ; i++) { + int len; + if (defaults[i].type == def_none) { + continue; + } + len = strlen(defaults[i].name); + if (len > maxlen && len < 80) { + maxlen = len; + } + } + + // 3/3/98 explain format of file + + fprintf(f,"# Doom config file\n"); + fprintf(f,"# Format:\n"); + fprintf(f,"# variable value\n"); + + for (i = 0 ; i < numdefaults ; i++) { + if (defaults[i].type == def_none) { + // CPhipps - pure headers + fprintf(f, "\n# %s\n", defaults[i].name); + } else + // e6y: arrays + if (defaults[i].type == def_arr) + { + int k; + fprintf (f,"%-*s \"%s\"\n",maxlen,defaults[i].name,*(defaults[i].location.ppsz)); + for (k = 0; k < *(defaults[i].location.array_size); k++) + { + char ***arr = defaults[i].location.array_data; + if ((*arr)[k]) + { + char def[80]; + sprintf(def, "%s%d", *(defaults[i].location.ppsz), k); + fprintf (f,"%-*s \"%s\"\n",maxlen,def, (*arr)[k]); + } + } + i += defaults[i].defaultvalue.array_size; + } + else + + // CPhipps - modified for new default_t form + if (!IS_STRING(defaults[i])) //jff 4/10/98 kill super-hack on pointer value + { + // CPhipps - remove keycode hack + // killough 3/6/98: use spaces instead of tabs for uniform justification + if (defaults[i].type == def_hex) + fprintf (f,"%-*s 0x%x\n",maxlen,defaults[i].name,*(defaults[i].location.pi)); + else + fprintf (f,"%-*s %i\n",maxlen,defaults[i].name,*(defaults[i].location.pi)); + } + else + { + fprintf (f,"%-*s \"%s\"\n",maxlen,defaults[i].name,*(defaults[i].location.ppsz)); + } + } + + fclose (f); +} + +/* + * M_LookupDefault + * + * cph - mimic MBF function for now. Yes it's crap. + */ + +struct default_s *M_LookupDefault(const char *name) +{ + int i; + for (i = 0 ; i < numdefaults ; i++) + { + if ((defaults[i].type != def_none) && !strcmp(name, defaults[i].name)) + return &defaults[i]; + } + + I_Error("M_LookupDefault: %s not found",name); + return NULL; +} + +// +// M_LoadDefaults +// + +#define NUMCHATSTRINGS 10 // phares 4/13/98 + +#define CFG_BUFFERMAX 32000 + +void M_LoadDefaults (void) +{ + int i; + int len; + FILE* f; + char def[80]; + char* strparm = malloc(CFG_BUFFERMAX); + char* cfgline = malloc(CFG_BUFFERMAX); + char* newstring = NULL; // killough + int parm; + dboolean isstring; + // e6y: arrays + default_t *item = NULL; + + // set everything to base values + + numdefaults = sizeof(defaults)/sizeof(defaults[0]); + for (i = 0 ; i < numdefaults ; i++) { + if (defaults[i].location.ppsz) + *defaults[i].location.ppsz = strdup(defaults[i].defaultvalue.psz); + if (defaults[i].location.pi) + *defaults[i].location.pi = defaults[i].defaultvalue.i; + } + + //e6y: arrays + for (i = 0 ; i < numdefaults ; i++) { + if (defaults[i].type == def_arr) + { + int k; + default_t *item = &defaults[i]; + char ***arr = (char***)(item->location.array_data); + // free memory + for (k = 0; k < *(item->location.array_size); k++) + { + if ((*arr)[k]) + { + free((*arr)[k]); + (*arr)[k] = NULL; + } + } + free(*arr); + *arr = NULL; + *(item->location.array_size) = 0; + // load predefined data + *arr = realloc(*arr, sizeof(char*) * item->defaultvalue.array_size); + *(item->location.array_size) = item->defaultvalue.array_size; + item->location.array_index = 0; + for (k = 0; k < item->defaultvalue.array_size; k++) + { + if (item->defaultvalue.array_data[k]) + (*arr)[k] = strdup(item->defaultvalue.array_data[k]); + else + (*arr)[k] = strdup(""); + } + } + } + + // check for a custom default file + +#define BOOM_CFG "prboom-plus.cfg" + + i = M_CheckParm ("-config"); + if (i && i < myargc-1) + { + defaultfile = strdup(myargv[i+1]); + } + else + { + const char* exedir = I_DoomExeDir(); + /* get config file from same directory as executable */ + int len = doom_snprintf(NULL, 0, "%s/" BOOM_CFG, exedir); + defaultfile = malloc(len+1); + doom_snprintf(defaultfile, len+1, "%s/" BOOM_CFG, exedir); + } + + lprintf (LO_CONFIRM, " default file: %s\n",defaultfile); + + // read the file in, overriding any set defaults + + f = M_fopen (defaultfile, "r"); + if (f) + { + while (!feof(f)) + { + isstring = false; + parm = 0; + fgets(cfgline, CFG_BUFFERMAX, f); + if (sscanf (cfgline, "%79s %[^\n]\n", def, strparm) == 2) + { + + //jff 3/3/98 skip lines not starting with an alphanum + + if (!isalnum(def[0])) + continue; + + if (strparm[0] == '"') { + // get a string default + + isstring = true; + len = strlen(strparm); + newstring = malloc(len); + strparm[len-1] = 0; // clears trailing double-quote mark + strcpy(newstring, strparm+1); // clears leading double-quote mark + } else if ((strparm[0] == '0') && (strparm[1] == 'x')) { + // CPhipps - allow ints to be specified in hex + sscanf(strparm+2, "%x", &parm); + } else { + sscanf(strparm, "%i", &parm); + // Keycode hack removed + } + + // e6y: arrays + if (item) + { + int *pcount = item->location.array_size; + int *index = &item->location.array_index; + char ***arr = (char***)(item->location.array_data); + if (!strncmp(def, *(item->location.ppsz), strlen(*(item->location.ppsz))) + && ((item->maxvalue == UL) || *(item->location.array_size) < item->maxvalue) ) + { + if ((*index) + 1 > *pcount) + { + *arr = realloc(*arr, sizeof(char*) * ((*index) + 1)); + (*pcount)++; + } + else + { + if ((*arr)[(*index)]) + { + free((*arr)[(*index)]); + (*arr)[(*index)] = NULL; + } + } + (*arr)[(*index)] = newstring; + (*index)++; + continue; + } + else + { + item = NULL; + } + } + + for (i = 0 ; i < numdefaults ; i++) + if ((defaults[i].type != def_none) && !strcmp(def, defaults[i].name)) + { + // e6y: arrays + if (defaults[i].type == def_arr) + { + union { const char **c; char **s; } u; // type punning via unions + + u.c = defaults[i].location.ppsz; + free(*(u.s)); + *(u.s) = newstring; + + item = &defaults[i]; + continue; + } + + // CPhipps - safety check + if (isstring != IS_STRING(defaults[i])) { + lprintf(LO_WARN, "M_LoadDefaults: Type mismatch reading %s\n", defaults[i].name); + continue; + } + if (!isstring) + { + + //jff 3/4/98 range check numeric parameters + + if ((defaults[i].minvalue==UL || defaults[i].minvalue<=parm) && + (defaults[i].maxvalue==UL || defaults[i].maxvalue>=parm)) + *(defaults[i].location.pi) = parm; + } + else + { + union { const char **c; char **s; } u; // type punning via unions + + u.c = defaults[i].location.ppsz; + free(*(u.s)); + *(u.s) = newstring; + } + break; + } + } + } + + fclose (f); + } + + free(strparm); + free(cfgline); + + //jff 3/4/98 redundant range checks for hud deleted here + /* proff 2001/7/1 - added prboom.wad as last entry so it's always loaded and + doesn't overlap with the cfg settings */ + //e6y: Check on existence of prboom.wad + if (!(wad_files[0] = I_FindFile(PACKAGE_TARNAME ".wad", ""))) + I_Error("PrBoom-Plus.wad not found. Can't continue."); +} + + +// +// SCREEN SHOTS +// + +// +// M_ScreenShot +// +// Modified by Lee Killough so that any number of shots can be taken, +// the code is faster, and no annoying "screenshot" message appears. + +// CPhipps - modified to use its own buffer for the image +// - checks for the case where no file can be created (doesn't occur on POSIX systems, would on DOS) +// - track errors better +// - split into 2 functions + +// +// M_DoScreenShot +// Takes a screenshot into the names file + +const char *screenshot_dir; + +void M_DoScreenShot (const char* fname) +{ + if (I_ScreenShot(fname) != 0) + doom_printf("M_ScreenShot: Error writing screenshot\n"); +} + +#ifndef SCREENSHOT_DIR +#define SCREENSHOT_DIR "." +#endif + +#ifdef HAVE_LIBSDL2_IMAGE +#define SCREENSHOT_EXT ".png" +#else +#define SCREENSHOT_EXT ".bmp" +#endif + +const char* M_CheckWritableDir(const char *dir) +{ + static char *base = NULL; + static int base_len = 0; + + const char *result = NULL; + int len; + + if (!dir || !(len = strlen(dir))) + { + return NULL; + } + + if (len + 1 > base_len) + { + base_len = len + 1; + base = malloc(len + 1); + } + + if (base) + { + strcpy(base, dir); + + if (base[len - 1] != '\\' && base[len - 1] != '/') + strcat(base, "/"); + if (!M_access(base, O_RDWR)) + { + base[strlen(base) - 1] = 0; + result = base; + } + } + + return result; +} + +void M_ScreenShot(void) +{ + static int shot; + char *lbmname = NULL; + int startshot; + const char *shot_dir = NULL; + int p; + int success = 0; + + if ((p = M_CheckParm("-shotdir")) && (p < myargc - 1)) + shot_dir = M_CheckWritableDir(myargv[p + 1]); + if (!shot_dir) + shot_dir = M_CheckWritableDir(screenshot_dir); + if (!shot_dir) +#ifdef _WIN32 + shot_dir = M_CheckWritableDir(I_DoomExeDir()); +#else + shot_dir = (!M_access(SCREENSHOT_DIR, 2) ? SCREENSHOT_DIR : NULL); +#endif + + if (shot_dir) + { + startshot = shot; // CPhipps - prevent infinite loop + + do { + int size = doom_snprintf(NULL, 0, "%s/doom%02d" SCREENSHOT_EXT, shot_dir, shot); + lbmname = realloc(lbmname, size+1); + doom_snprintf(lbmname, size+1, "%s/doom%02d" SCREENSHOT_EXT, shot_dir, shot); + shot++; + } while (!M_access(lbmname,0) && (shot != startshot) && (shot < 10000)); + + if (M_access(lbmname,0)) + { + S_StartSound(NULL,gamemode==commercial ? sfx_radio : sfx_tink); + M_DoScreenShot(lbmname); // cph + success = 1; + } + free(lbmname); + if (success) return; + } + + doom_printf ("M_ScreenShot: Couldn't create screenshot"); + return; +} + +int M_StrToInt(const char *s, int *l) +{ + return ( + (sscanf(s, " 0x%x", l) == 1) || + (sscanf(s, " 0X%x", l) == 1) || + (sscanf(s, " 0%o", l) == 1) || + (sscanf(s, " %d", l) == 1) + ); +} + +int M_StrToFloat(const char *s, float *f) +{ + return ( + (sscanf(s, " %f", f) == 1) + ); +} + +int M_DoubleToInt(double x) +{ +#ifdef __GNUC__ + double tmp = x; + return (int)tmp; +#else + return (int)x; +#endif +} + +char* M_Strlwr(char* str) +{ + char* p; + for (p=str; *p; p++) *p = tolower(*p); + return str; +} + +char* M_Strupr(char* str) +{ + char* p; + for (p=str; *p; p++) *p = toupper(*p); + return str; +} + +char *M_StrRTrim(char *str) +{ + char *end; + + if (str) + { + // Trim trailing space + end = str + strlen(str) - 1; + while(end > str && isspace(*end)) + { + end--; + } + + // Write new null terminator + *(end + 1) = 0; + } + + return str; +} + +void M_ArrayClear(array_t *data) +{ + data->count = 0; +} + +void M_ArrayFree(array_t *data) +{ + if (data->data) + { + free(data->data); + data->data = NULL; + } + + data->capacity = 0; + data->count = 0; +} + +void M_ArrayAddItem(array_t *data, void *item, int itemsize) +{ + if (data->count + 1 >= data->capacity) + { + data->capacity = (data->capacity ? data->capacity * 2 : 128); + data->data = realloc(data->data, data->capacity * itemsize); + } + + memcpy((unsigned char*)data->data + data->count * itemsize, item, itemsize); + data->count++; +} + +void* M_ArrayGetNewItem(array_t *data, int itemsize) +{ + if (data->count + 1 >= data->capacity) + { + data->capacity = (data->capacity ? data->capacity * 2 : 128); + data->data = realloc(data->data, data->capacity * itemsize); + } + + data->count++; + + return (unsigned char*)data->data + (data->count - 1) * itemsize; +} diff --git a/src/m_misc.h b/src/m_misc.h new file mode 100644 index 0000000..67b23cb --- /dev/null +++ b/src/m_misc.h @@ -0,0 +1,142 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * External non-system-specific stuff, like storing config settings, + * simple file handling, and saving screnshots. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __M_MISC__ +#define __M_MISC__ + + +#include "doomtype.h" +// +// MISC +// + +dboolean M_WriteFile (char const* name, const void* source, size_t length); + +int M_ReadFile (char const* name,byte** buffer); + +void M_ScreenShot (void); +void M_DoScreenShot (const char*); // cph + +void M_LoadDefaults (void); + +void M_SaveDefaults (void); + +struct default_s *M_LookupDefault(const char *name); /* killough 11/98 */ + +// phares 4/21/98: +// Moved from m_misc.c so m_menu.c could see it. + +// CPhipps - struct to hold a value in a config file +// Cannot be a union, as it must be initialised +typedef struct default_s +{ + const char* name; + /* cph - + * The location struct holds the pointer to the variable holding the + * setting. For int's we do nothing special. + * For strings, the string is actually stored on our heap with Z_Strdup() + * BUT we don't want the rest of the program to be able to modify them, + * so we declare it const. It's not really const though, and m_misc.c and + * m_menu.c cast it back when they need to change it. Possibly this is + * more trouble than it's worth. + */ + // Note: casts are now made via unions to avoid discarding qualifier warnings + struct { + int* pi; + const char** ppsz; + //e6y: arrays + int* array_size; + char*** array_data; + int array_index; + } location; + struct { + int i; + const char* psz; + //e6y: arrays + int array_size; + const char** array_data; + } defaultvalue; // CPhipps - default value + // Limits (for an int) + int minvalue; // jff 3/3/98 minimum allowed value + int maxvalue; // jff 3/3/98 maximum allowed value + enum { + def_none, // Dummy entry + def_str, // A string + def_int, // Integer + def_hex, // Integer (write in hex) + def_arr, // e6y: arrays + def_bool = def_int, // Boolean + def_key = def_hex, // Key code (byte) + def_mouseb = def_int,// Mouse button + def_colour = def_hex // Colour (256 colour palette entry) + } type; // CPhipps - type of entry + int setupscreen; // phares 4/19/98: setup screen where this appears + int *current; /* cph - MBF-like pointer to current value */ + // cph - removed the help strings from the config file + // const char* help; // jff 3/3/98 description of parameter + // CPhipps - remove unused "lousy hack" code + struct setup_menu_s *setup_menu; /* Xref to setup menu item, if any */ +} default_t; + +#define IS_STRING(dv) ((dv).type == def_str) +// CPhipps - What is the max. key code that X will send us? +#define MAX_KEY 65536 +#define MAX_MOUSEB 8 + +#define UL (-123456789) /* magic number for no min or max for parameter */ + +int M_StrToInt(const char *s, int *l); +int M_StrToFloat(const char *s, float *f); + +int M_DoubleToInt(double x); + +char* M_Strlwr(char* str); +char* M_Strupr(char* str); +char* M_StrRTrim(char* str); + +extern const char *screenshot_dir; + +typedef struct array_s +{ + void *data; + int capacity; + int count; +} array_t; +void M_ArrayClear(array_t *data); +void M_ArrayFree(array_t *data); +void M_ArrayAddItem(array_t *data, void *item, int itemsize); +void* M_ArrayGetNewItem(array_t *data, int itemsize); + +#endif diff --git a/src/m_random.c b/src/m_random.c new file mode 100644 index 0000000..3586edb --- /dev/null +++ b/src/m_random.c @@ -0,0 +1,147 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Random number LUT. + * + * 1/19/98 killough: Rewrote random number generator for better randomness, + * while at the same time maintaining demo sync and backward compatibility. + * + * 2/16/98 killough: Made each RNG local to each control-equivalent block, + * to reduce the chances of demo sync problems. + * + *-----------------------------------------------------------------------------*/ + + +#include "doomstat.h" +#include "m_random.h" +#include "lprintf.h" + +// +// M_Random +// Returns a 0-255 number +// +static const unsigned char rndtable[256] = { // 1/19/98 killough -- made const + 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 +}; + +rng_t rng; // the random number state + +unsigned int rngseed = 1993; // killough 3/26/98: The seed + +int (P_Random)(pr_class_t pr_class +#ifdef INSTRUMENTED + , const char *file, int line +#endif +) +{ + // killough 2/16/98: We always update both sets of random number + // generators, to ensure repeatability if the demo_compatibility + // flag is changed while the program is running. Changing the + // demo_compatibility flag does not change the sequences generated, + // only which one is selected from. + // + // All of this RNG stuff is tricky as far as demo sync goes -- + // it's like playing with explosives :) Lee + +#ifdef INSTRUMENTED + //lprintf(LO_DEBUG, "%.10d: %.10d - %s:%.5d\n", gametic, pr_class, file, line); +#endif + + int compat = pr_class == pr_misc ? + (rng.prndindex = (rng.prndindex + 1) & 255) : + (rng. rndindex = (rng. rndindex + 1) & 255) ; + + unsigned long boom; + + // killough 3/31/98: + // If demo sync insurance is not requested, use + // much more unstable method by putting everything + // except pr_misc into pr_all_in_one + + if (pr_class != pr_misc && !demo_insurance) // killough 3/31/98 + pr_class = pr_all_in_one; + + boom = rng.seed[pr_class]; + + // killough 3/26/98: add pr_class*2 to addend + + rng.seed[pr_class] = boom * 1664525ul + 221297ul + pr_class*2; + + if (demo_compatibility) + return rndtable[compat]; + + boom >>= 20; + + /* killough 3/30/98: use gametic-levelstarttic to shuffle RNG + * killough 3/31/98: but only if demo insurance requested, + * since it's unnecessary for random shuffling otherwise + * killough 9/29/98: but use basetic now instead of levelstarttic + * cph - DEMOSYNC - this change makes MBF demos work, + * but does it break Boom ones? + */ + + if (demo_insurance) + boom += (gametic-basetic)*7; + + return boom & 255; +} + +// Initialize all the seeds +// +// This initialization method is critical to maintaining demo sync. +// Each seed is initialized according to its class, so if new classes +// are added they must be added to end of pr_class_t list. killough +// + +void M_ClearRandom (void) +{ + int i; + unsigned int seed = rngseed*2+1; // add 3/26/98: add rngseed + for (i=0; i +#ifdef __arch__swab16 +#define doom_swap_s (signed short)__arch__swab16 +#endif +#ifdef __arch__swab32 +#define doom_swap_l (signed long)__arch__swab32 +#endif +#endif /* HAVE_ASM_BYTEORDER_H */ + +#ifdef HAVE_LIBKERN_OSBYTEORDER_H +#include + +#define doom_swap_s (short)OSSwapInt16 +#define doom_swap_l (long)OSSwapInt32 +#endif + +#ifndef doom_swap_l +#define doom_swap_l(x) \ + ((long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \ + (((unsigned long int)(x) & 0x0000ff00U) << 8) | \ + (((unsigned long int)(x) & 0x00ff0000U) >> 8) | \ + (((unsigned long int)(x) & 0xff000000U) >> 24))) +#endif + +#ifndef doom_swap_s +#define doom_swap_s(x) \ + ((short int)((((unsigned short int)(x) & 0x00ff) << 8) | \ + (((unsigned short int)(x) & 0xff00) >> 8))) +#endif + +/* Macros are named doom_XtoYT, where + * X is thing to convert from, Y is thing to convert to, chosen from + * n for network, h for host (i.e our machine's), w for WAD (Doom data files) + * and T is the type, l or s for long or short + * + * CPhipps - all WADs and network packets will be little endian for now + * Use separate macros so network could be converted to big-endian later. + */ + +#ifdef WORDS_BIGENDIAN + +#define doom_wtohl(x) doom_swap_l(x) +#define doom_htowl(x) doom_swap_l(x) +#define doom_wtohs(x) doom_swap_s(x) +#define doom_htows(x) doom_swap_s(x) + +#define doom_ntohl(x) doom_swap_l(x) +#define doom_htonl(x) doom_swap_l(x) +#define doom_ntohs(x) doom_swap_s(x) +#define doom_htons(x) doom_swap_s(x) + +#else + +#define doom_wtohl(x) (long int)(x) +#define doom_htowl(x) (long int)(x) +#define doom_wtohs(x) (short int)(x) +#define doom_htows(x) (short int)(x) + +#define doom_ntohl(x) (long int)(x) +#define doom_htonl(x) (long int)(x) +#define doom_ntohs(x) (short int)(x) +#define doom_htons(x) (short int)(x) + +#endif + +/* CPhipps - Boom's old LONG and SHORT endianness macros are for WAD stuff */ + +#define LittleLong(x) doom_wtohl(x) +#define LittleShort(x) doom_htows(x) + +#endif diff --git a/src/md5.c b/src/md5.c new file mode 100644 index 0000000..6d51e05 --- /dev/null +++ b/src/md5.c @@ -0,0 +1,240 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' header + * definitions; now uses stuff from dpkg's config.h. + * - Ian Jackson . + * Still in the public domain. + */ +#include "config.h" + +#include /* for memcpy() */ +#include /* for stupid systems */ + +#include "md5.h" + +#ifdef WORDS_BIGENDIAN +void +byteSwap(UWORD32 *buf, unsigned words) +{ + md5byte *p = (md5byte *)buf; + + do { + *buf++ = (UWORD32)((unsigned)p[3] << 8 | p[2]) << 16 | + ((unsigned)p[1] << 8 | p[0]); + p += 4; + } while (--words); +} +#else +#define byteSwap(buf,words) +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len) +{ + UWORD32 t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if (t > len) { + memcpy((md5byte *)ctx->in + 64 - t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((md5byte *)ctx->in + 64 - t, buf, t); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Final(md5byte digest[16], struct MD5Context *ctx) +{ + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + md5byte *p = (md5byte *)ctx->in + count; + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count + 8); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + p = (md5byte *)ctx->in; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + MD5Transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ + (w += f(x,y,z) + in, w = (w<>(32-s)) + x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +MD5Transform(UWORD32 buf[4], UWORD32 const in[16]) +{ + register UWORD32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif diff --git a/src/md5.h b/src/md5.h new file mode 100644 index 0000000..3ebeb36 --- /dev/null +++ b/src/md5.h @@ -0,0 +1,47 @@ +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' + * header definitions; now uses stuff from dpkg's config.h + * - Ian Jackson . + * Still in the public domain. + */ + +#ifndef MD5_H +#define MD5_H + +#ifdef _MSC_VER +#define WIN32_LEAN_AND_MEAN +#include +#define UWORD32 DWORD +#else +#include +#define UWORD32 uint32_t +#endif +#define md5byte unsigned char + +struct MD5Context { + UWORD32 buf[4]; + UWORD32 bytes[2]; + UWORD32 in[16]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +void MD5Transform(UWORD32 buf[4], UWORD32 const in[16]); + +#endif /* !MD5_H */ diff --git a/src/memio.c b/src/memio.c new file mode 100644 index 0000000..1b619a1 --- /dev/null +++ b/src/memio.c @@ -0,0 +1,206 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// Emulates the IO functions in C stdio.h reading and writing to +// memory. +// + +// e6y +// All tabs are replaced with spaces. +// Fixed eol style of files. + +#include +#include +#include + +#include "memio.h" + +#include "z_zone.h" + +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; +}; + +// Open a memory area for reading + +MEMFILE *mem_fopen_read(const 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 %i\n", newpos); + return -1; + } +} diff --git a/src/memio.h b/src/memio.h new file mode 100644 index 0000000..63ead7d --- /dev/null +++ b/src/memio.h @@ -0,0 +1,48 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// + +// e6y +// All tabs are replaced with spaces. +// Fixed eol style of files. + +#ifndef MEMIO_H +#define MEMIO_H + +typedef struct _MEMFILE MEMFILE; + +typedef enum +{ + MEM_SEEK_SET, + MEM_SEEK_CUR, + MEM_SEEK_END, +} mem_rel_t; + +MEMFILE *mem_fopen_read(const 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 /* #ifndef MEMIO_H */ diff --git a/src/mus2mid.c b/src/mus2mid.c new file mode 100644 index 0000000..9b8d19d --- /dev/null +++ b/src/mus2mid.c @@ -0,0 +1,685 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005 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. +// +// 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// 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. + +// e6y +// All tabs are replaced with spaces. +// Fixed eol style of files. + +#include + +#include "doomtype.h" +#include "m_swap.h" + +#include "memio.h" +#include "mus2mid.h" + +#define NUM_CHANNELS 16 + +#define MIDI_PERCUSSION_CHAN 9 +#define MUS_PERCUSSION_CHAN 15 + +// 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; + +// 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]; + +// Write timestamp to a MIDI file. + +static dboolean WriteTime(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 dboolean WriteEndTrack(MEMFILE *midioutput) +{ + byte endtrack[] = {0xFF, 0x2F, 0x00}; + + if (WriteTime(queuedtime, midioutput)) + { + return true; + } + + if (mem_fwrite(endtrack, 1, 3, midioutput) != 3) + { + return true; + } + + tracksize += 3; + return false; +} + +// Write a key press event +static dboolean WritePressKey(byte channel, byte key, + byte velocity, MEMFILE *midioutput) +{ + byte working = midi_presskey | channel; + + if (WriteTime(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 dboolean WriteReleaseKey(byte channel, byte key, + MEMFILE *midioutput) +{ + byte working = midi_releasekey | channel; + + if (WriteTime(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 dboolean WritePitchWheel(byte channel, short wheel, + MEMFILE *midioutput) +{ + byte working = midi_pitchwheel | channel; + + if (WriteTime(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 dboolean WriteChangePatch(byte channel, byte patch, + MEMFILE *midioutput) +{ + byte working = midi_changepatch | channel; + + if (WriteTime(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 dboolean WriteChangeController_Valued(byte channel, + byte control, + byte value, + MEMFILE *midioutput) +{ + byte working = midi_changecontroller | channel; + + if (WriteTime(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 dboolean WriteChangeController_Valueless(byte channel, + byte control, + MEMFILE *midioutput) +{ + return WriteChangeController_Valued(channel, control, 0, + midioutput); +} + +// Allocate a free MIDI channel. + +static int AllocateMIDIChannel(void) +{ + int result; + int max; + int i; + + // Find the current highest-allocated channel. + + max = -1; + + for (i=0; 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 GetMIDIChannel(int mus_channel) +{ + // 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] = AllocateMIDIChannel(); + } + + return channel_map[mus_channel]; + } +} + +static dboolean ReadMusHeader(MEMFILE *file, musheader *header) +{ + dboolean 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 = LittleShort(header->scorelength); + header->scorestart = LittleShort(header->scorestart); + header->primarychannels = LittleShort(header->primarychannels); + header->secondarychannels = LittleShort(header->secondarychannels); + header->instrumentcount = LittleShort(header->instrumentcount); + } + + return result; +} + + +// 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. + +dboolean mus2mid(MEMFILE *musinput, MEMFILE *midioutput) +{ + // Header for the MUS file + musheader musfileheader; + + // Descriptor for the current MUS event + byte eventdescriptor; + byte 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 14) + { + return true; + } + + if (WriteChangeController_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 (WriteChangePatch(channel, controllervalue, + midioutput)) + { + return true; + } + } + else + { + if (controllernumber < 1 || controllernumber > 9) + { + return true; + } + + if (WriteChangeController_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 (WriteEndTrack(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; +} diff --git a/src/mus2mid.h b/src/mus2mid.h new file mode 100644 index 0000000..c9f0940 --- /dev/null +++ b/src/mus2mid.h @@ -0,0 +1,50 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005 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. +// +// 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// +// 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. + +// e6y +// All tabs are replaced with spaces. +// Fixed eol style of files. + +#ifndef MUS2MID_H +#define MUS2MID_H + +#include "doomtype.h" +#include "memio.h" + +// Structure to hold MUS file header +typedef struct +{ + byte id[4]; + unsigned short scorelength; + unsigned short scorestart; + unsigned short primarychannels; + unsigned short secondarychannels; + unsigned short instrumentcount; +} musheader; + +dboolean mus2mid(MEMFILE *musinput, MEMFILE *midioutput); + +#endif /* #ifndef MUS2MID_H */ diff --git a/src/p_ceilng.c b/src/p_ceilng.c new file mode 100644 index 0000000..a64bed2 --- /dev/null +++ b/src/p_ceilng.c @@ -0,0 +1,477 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Ceiling aninmation (lowering, crushing, raising) + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "r_main.h" +#include "p_spec.h" +#include "p_tick.h" +#include "s_sound.h" +#include "sounds.h" +#include "e6y.h"//e6y + +// the list of ceilings moving currently, including crushers +ceilinglist_t *activeceilings; + +///////////////////////////////////////////////////////////////// +// +// Ceiling action routine and linedef type handler +// +///////////////////////////////////////////////////////////////// + +// +// T_MoveCeiling +// +// Action routine that moves ceilings. Called once per tick. +// +// Passed a ceiling_t structure that contains all the info about the move. +// see P_SPEC.H for fields. No return. +// +// jff 02/08/98 all cases with labels beginning with gen added to support +// generalized line type behaviors. +// +void T_MoveCeiling (ceiling_t* ceiling) +{ + result_e res; + + switch(ceiling->direction) + { + case 0: + // If ceiling in stasis, do nothing + break; + + case 1: + // Ceiling is moving up + res = T_MovePlane + ( + ceiling->sector, + ceiling->speed, + ceiling->topheight, + false, + 1, + ceiling->direction + ); + + // if not a silent crusher, make moving sound + if (!(leveltime&7)) + { + switch(ceiling->type) + { + case silentCrushAndRaise: + case genSilentCrusher: + break; + default: + S_StartSound((mobj_t *)&ceiling->sector->soundorg,sfx_stnmov); + break; + } + } + + // handle reaching destination height + if (res == pastdest) + { + switch(ceiling->type) + { + // plain movers are just removed + case raiseToHighest: + case genCeiling: + P_RemoveActiveCeiling(ceiling); + break; + + // movers with texture change, change the texture then get removed + case genCeilingChgT: + case genCeilingChg0: + ceiling->sector->special = ceiling->newspecial; + //jff 3/14/98 transfer old special field as well + ceiling->sector->oldspecial = ceiling->oldspecial; + // fallthrough + case genCeilingChg: + ceiling->sector->ceilingpic = ceiling->texture; + P_RemoveActiveCeiling(ceiling); + break; + + // crushers reverse direction at the top + case silentCrushAndRaise: + S_StartSound((mobj_t *)&ceiling->sector->soundorg,sfx_pstop); + // fallthrough + case genSilentCrusher: + case genCrusher: + case fastCrushAndRaise: + case crushAndRaise: + ceiling->direction = -1; + break; + + default: + break; + } + } + break; + + case -1: + // Ceiling moving down + res = T_MovePlane + ( + ceiling->sector, + ceiling->speed, + ceiling->bottomheight, + ceiling->crush, + 1, + ceiling->direction + ); + + // if not silent crusher type make moving sound + if (!(leveltime&7)) + { + switch(ceiling->type) + { + case silentCrushAndRaise: + case genSilentCrusher: + break; + default: + S_StartSound((mobj_t *)&ceiling->sector->soundorg,sfx_stnmov); + } + } + + // handle reaching destination height + if (res == pastdest) + { + switch(ceiling->type) + { + // 02/09/98 jff change slow crushers' speed back to normal + // start back up + case genSilentCrusher: + case genCrusher: + if (ceiling->oldspeedspeed = ceiling->oldspeed; + ceiling->direction = 1; //jff 2/22/98 make it go back up! + break; + + // make platform stop at bottom of all crusher strokes + // except generalized ones, reset speed, start back up + case silentCrushAndRaise: + S_StartSound((mobj_t *)&ceiling->sector->soundorg,sfx_pstop); + // fallthrough + case crushAndRaise: + ceiling->speed = CEILSPEED; + // fallthrough + case fastCrushAndRaise: + ceiling->direction = 1; + break; + + // in the case of ceiling mover/changer, change the texture + // then remove the active ceiling + case genCeilingChgT: + case genCeilingChg0: + ceiling->sector->special = ceiling->newspecial; + //jff add to fix bug in special transfers from changes + ceiling->sector->oldspecial = ceiling->oldspecial; + // fallthrough + case genCeilingChg: + ceiling->sector->ceilingpic = ceiling->texture; + P_RemoveActiveCeiling(ceiling); + break; + + // all other case, just remove the active ceiling + case lowerAndCrush: + case lowerToFloor: + case lowerToLowest: + case lowerToMaxFloor: + case genCeiling: + P_RemoveActiveCeiling(ceiling); + break; + + default: + break; + } + } + else // ( res != pastdest ) + { + // handle the crusher encountering an obstacle + if (res == crushed) + { + switch(ceiling->type) + { + //jff 02/08/98 slow down slow crushers on obstacle + case genCrusher: + case genSilentCrusher: + if (ceiling->oldspeed < CEILSPEED*3) + ceiling->speed = CEILSPEED / 8; + break; + case silentCrushAndRaise: + case crushAndRaise: + case lowerAndCrush: + ceiling->speed = CEILSPEED / 8; + break; + + default: + break; + } + } + } + break; + } +} + + +// +// EV_DoCeiling +// +// Move a ceiling up/down or start a crusher +// +// Passed the linedef activating the function and the type of function desired +// returns true if a thinker started +// +int EV_DoCeiling +( line_t* line, + ceiling_e type ) +{ + int secnum; + int rtn; + sector_t* sec; + ceiling_t* ceiling; + + secnum = -1; + rtn = 0; + + if (ProcessNoTagLines(line, &sec, &secnum)) {if (zerotag_manual) goto manual_ceiling; else {return rtn;}};//e6y + // Reactivate in-stasis ceilings...for certain types. + // This restarts a crusher after it has been stopped + switch(type) + { + case fastCrushAndRaise: + case silentCrushAndRaise: + case crushAndRaise: + //jff 4/5/98 return if activated + rtn = P_ActivateInStasisCeiling(line); + default: + break; + } + + // affects all sectors with the same tag as the linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_ceiling://e6y + // if ceiling already moving, don't start a second function on it + if (P_SectorActive(ceiling_special,sec)) { //jff 2/22/98 + if (!zerotag_manual) continue; else {return rtn;}};//e6y + + // create a new ceiling thinker + rtn = 1; + ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVSPEC, 0); + memset(ceiling, 0, sizeof(*ceiling)); + P_AddThinker (&ceiling->thinker); + sec->ceilingdata = ceiling; //jff 2/22/98 + ceiling->thinker.function = T_MoveCeiling; + ceiling->sector = sec; + ceiling->crush = false; + + // setup ceiling structure according to type of function + switch(type) + { + case fastCrushAndRaise: + ceiling->crush = true; + ceiling->topheight = sec->ceilingheight; + ceiling->bottomheight = sec->floorheight + (8*FRACUNIT); + ceiling->direction = -1; + ceiling->speed = CEILSPEED * 2; + break; + + case silentCrushAndRaise: + case crushAndRaise: + ceiling->crush = true; + ceiling->topheight = sec->ceilingheight; + // fallthrough + case lowerAndCrush: + case lowerToFloor: + ceiling->bottomheight = sec->floorheight; + if (type != lowerToFloor) + ceiling->bottomheight += 8*FRACUNIT; + ceiling->direction = -1; + ceiling->speed = CEILSPEED; + break; + + case raiseToHighest: + ceiling->topheight = P_FindHighestCeilingSurrounding(sec); + ceiling->direction = 1; + ceiling->speed = CEILSPEED; + break; + + case lowerToLowest: + ceiling->bottomheight = P_FindLowestCeilingSurrounding(sec); + ceiling->direction = -1; + ceiling->speed = CEILSPEED; + break; + + case lowerToMaxFloor: + ceiling->bottomheight = P_FindHighestFloorSurrounding(sec); + ceiling->direction = -1; + ceiling->speed = CEILSPEED; + break; + + default: + break; + } + + // add the ceiling to the active list + ceiling->tag = sec->tag; + ceiling->type = type; + P_AddActiveCeiling(ceiling); + if (zerotag_manual) return rtn; //e6y + } + return rtn; +} + +////////////////////////////////////////////////////////////////////// +// +// Active ceiling list primitives +// +///////////////////////////////////////////////////////////////////// + +// jff 2/22/98 - modified Lee's plat code to work for ceilings +// +// The following were all rewritten by Lee Killough +// to use the new structure which places no limits +// on active ceilings. It also avoids spending as much +// time searching for active ceilings. Previously a +// fixed-size array was used, with NULL indicating +// empty entries, while now a doubly-linked list +// is used. + +// +// P_ActivateInStasisCeiling() +// +// Reactivates all stopped crushers with the right tag +// +// Passed the line reactivating the crusher +// Returns true if a ceiling reactivated +// +//jff 4/5/98 return if activated +int P_ActivateInStasisCeiling(line_t *line) +{ + ceilinglist_t *cl; + int rtn=0; + + for (cl=activeceilings; cl; cl=cl->next) + { + ceiling_t *ceiling = cl->ceiling; + if (ceiling->tag == line->tag && ceiling->direction == 0) + { + ceiling->direction = ceiling->olddirection; + ceiling->thinker.function = T_MoveCeiling; + //jff 4/5/98 return if activated + rtn=1; + } + } + return rtn; +} + +// +// EV_CeilingCrushStop() +// +// Stops all active ceilings with the right tag +// +// Passed the linedef stopping the ceilings +// Returns true if a ceiling put in stasis +// +int EV_CeilingCrushStop(line_t* line) +{ + int rtn=0; + + ceilinglist_t *cl; + for (cl=activeceilings; cl; cl=cl->next) + { + ceiling_t *ceiling = cl->ceiling; + if (ceiling->direction != 0 && ceiling->tag == line->tag) + { + ceiling->olddirection = ceiling->direction; + ceiling->direction = 0; + ceiling->thinker.function = NULL; + rtn=1; + } + } + return rtn; +} + +// +// P_AddActiveCeiling() +// +// Adds a ceiling to the head of the list of active ceilings +// +// Passed the ceiling motion structure +// Returns nothing +// +void P_AddActiveCeiling(ceiling_t* ceiling) +{ + ceilinglist_t *list = malloc(sizeof *list); + list->ceiling = ceiling; + ceiling->list = list; + if ((list->next = activeceilings)) + list->next->prev = &list->next; + list->prev = &activeceilings; + activeceilings = list; +} + +// +// P_RemoveActiveCeiling() +// +// Removes a ceiling from the list of active ceilings +// +// Passed the ceiling motion structure +// Returns nothing +// +void P_RemoveActiveCeiling(ceiling_t* ceiling) +{ + ceilinglist_t *list = ceiling->list; + ceiling->sector->ceilingdata = NULL; //jff 2/22/98 + P_RemoveThinker(&ceiling->thinker); + if ((*list->prev = list->next)) + list->next->prev = list->prev; + free(list); +} + +// +// P_RemoveAllActiveCeilings() +// +// Removes all ceilings from the active ceiling list +// +// Passed nothing, returns nothing +// +void P_RemoveAllActiveCeilings(void) +{ + while (activeceilings) + { + ceilinglist_t *next = activeceilings->next; + free(activeceilings); + activeceilings = next; + } +} diff --git a/src/p_checksum.c b/src/p_checksum.c new file mode 100644 index 0000000..d4e5141 --- /dev/null +++ b/src/p_checksum.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include /* exit(), atexit() */ +#include "i_system.h" /* I_AtExit() */ + +#include "p_checksum.h" +#include "md5.h" +#include "doomstat.h" /* players{,ingame} */ +#include "lprintf.h" + +#include "m_io.h" + +/* forward decls */ +static void p_checksum_cleanup(void); +void checksum_gamestate(int tic); + +/* vars */ +static void p_checksum_nop(int tic){} /* do nothing */ +void (*P_Checksum)(int) = p_checksum_nop; + +/* + * P_RecordChecksum + * sets up the file and function pointers to write out checksum data + */ +static FILE *outfile = NULL; +static struct MD5Context md5global; + +void P_RecordChecksum(const char *file) { + size_t fnsize; + + fnsize = strlen(file); + + /* special case: write to stdout */ + if(0 == strncmp("-",file,MIN(1,fnsize))) + outfile = stdout; + else { + outfile = M_fopen(file,"wb"); + if(NULL == outfile) { + I_Error("cannot open %s for writing checksum:\n%s\n", + file, strerror(errno)); + } + I_AtExit(p_checksum_cleanup, true); + } + + MD5Init(&md5global); + + P_Checksum = checksum_gamestate; +} + +void P_ChecksumFinal(void) { + int i; + unsigned char digest[16]; + + if (!outfile) + return; + + MD5Final(digest, &md5global); + fprintf(outfile, "final: "); + for (i=0; i<16; i++) + fprintf(outfile,"%x", digest[i]); + fprintf(outfile, "\n"); + MD5Init(&md5global); +} + +static void p_checksum_cleanup(void) { + if (outfile && (outfile != stdout)) + fclose(outfile); +} + +/* + * runs on each tic when recording checksums + */ +void checksum_gamestate(int tic) { + int i; + struct MD5Context md5ctx; + unsigned char digest[16]; + char buffer[2048]; + + fprintf(outfile,"%6d, ", tic); + + /* based on "ArchivePlayers" */ + MD5Init(&md5ctx); + for (i=0 ; idirection) + { + case 0: + // Door is waiting + if (!--door->topcountdown) // downcount and check + { + switch(door->type) + { + case blazeRaise: + case genBlazeRaise: + door->direction = -1; // time to go back down + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdcls); + break; + + case normal: + case genRaise: + door->direction = -1; // time to go back down + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_dorcls); + break; + + case close30ThenOpen: + case genCdO: + door->direction = 1; // time to go back up + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_doropn); + break; + + case genBlazeCdO: + door->direction = 1; // time to go back up + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdopn); + break; + + default: + break; + } + } + break; + + case 2: + // Special case for sector type door that opens in 5 mins + if (!--door->topcountdown) // 5 minutes up? + { + switch(door->type) + { + case raiseIn5Mins: + door->direction = 1; // time to raise then + door->type = normal; // door acts just like normal 1 DR door now + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_doropn); + break; + + default: + break; + } + } + break; + + case -1: + // Door is moving down + res = T_MovePlane + ( + door->sector, + door->speed, + door->sector->floorheight, + false, + 1, + door->direction + ); + + /* killough 10/98: implement gradual lighting effects */ + // e6y: "Tagged doors don't trigger special lighting" handled wrong + // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 + // Old code: if (door->lighttag && door->topheight - door->sector->floorheight) + if (door->lighttag && door->topheight - door->sector->floorheight && compatibility_level >= mbf_compatibility) + EV_LightTurnOnPartway(door->line, + FixedDiv(door->sector->ceilingheight - + door->sector->floorheight, + door->topheight - + door->sector->floorheight)); + + // handle door reaching bottom + if (res == pastdest) + { + switch(door->type) + { + // regular open and close doors are all done, remove them + case blazeRaise: + case blazeClose: + case genBlazeRaise: + case genBlazeClose: + door->sector->ceilingdata = NULL; //jff 2/22/98 + P_RemoveThinker (&door->thinker); // unlink and free + // killough 4/15/98: remove double-closing sound of blazing doors + if (comp[comp_blazing]) + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdcls); + break; + + case normal: + case closeDoor: + case genRaise: + case genClose: + door->sector->ceilingdata = NULL; //jff 2/22/98 + P_RemoveThinker (&door->thinker); // unlink and free + break; + + // close then open doors start waiting + case close30ThenOpen: + door->direction = 0; + door->topcountdown = TICRATE*30; + break; + + case genCdO: + case genBlazeCdO: + door->direction = 0; + door->topcountdown = door->topwait; // jff 5/8/98 insert delay + break; + + default: + break; + } + // e6y: "Tagged doors don't trigger special lighting" handled wrong + // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 + if (door->lighttag && door->topheight - door->sector->floorheight && compatibility_level < mbf_compatibility) + EV_LightTurnOnPartway(door->line,0); + } + /* jff 1/31/98 turn lighting off in tagged sectors of manual doors + * killough 10/98: replaced with gradual lighting code + */ + else if (res == crushed) // handle door meeting obstruction on way down + { + switch(door->type) + { + case genClose: + case genBlazeClose: + case blazeClose: + case closeDoor: // Close types do not bounce, merely wait + break; + + case blazeRaise: + case genBlazeRaise: + door->direction = 1; + if (!comp[comp_blazing]) { + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdopn); + break; + } + // fallthrough + + default: // other types bounce off the obstruction + door->direction = 1; + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_doropn); + break; + } + } + break; + + case 1: + // Door is moving up + res = T_MovePlane + ( + door->sector, + door->speed, + door->topheight, + false, + 1, + door->direction + ); + + /* killough 10/98: implement gradual lighting effects */ + // e6y: "Tagged doors don't trigger special lighting" handled wrong + // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 + // Old code: if (door->lighttag && door->topheight - door->sector->floorheight) + if (door->lighttag && door->topheight - door->sector->floorheight && compatibility_level >= mbf_compatibility) + EV_LightTurnOnPartway(door->line, + FixedDiv(door->sector->ceilingheight - + door->sector->floorheight, + door->topheight - + door->sector->floorheight)); + + // handle door reaching the top + if (res == pastdest) + { + switch(door->type) + { + case blazeRaise: // regular open/close doors start waiting + case normal: + case genRaise: + case genBlazeRaise: + door->direction = 0; // wait at top with delay + door->topcountdown = door->topwait; + break; + + case close30ThenOpen: // close and close/open doors are done + case blazeOpen: + case openDoor: + case genBlazeOpen: + case genOpen: + case genCdO: + case genBlazeCdO: + door->sector->ceilingdata = NULL; //jff 2/22/98 + P_RemoveThinker (&door->thinker); // unlink and free + break; + + default: + break; + } + + /* jff 1/31/98 turn lighting on in tagged sectors of manual doors + * killough 10/98: replaced with gradual lighting code */ + // e6y: "Tagged doors don't trigger special lighting" handled wrong + // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 + if (door->lighttag && door->topheight - door->sector->floorheight && compatibility_level < mbf_compatibility) + EV_LightTurnOnPartway(door->line,FRACUNIT); + } + break; + } +} + +/////////////////////////////////////////////////////////////// +// +// Door linedef handlers +// +/////////////////////////////////////////////////////////////// + +// +// EV_DoLockedDoor +// +// Handle opening a tagged locked door +// +// Passed the line activating the door, the type of door, +// and the thing that activated the line +// Returns true if a thinker created +// +int EV_DoLockedDoor +( line_t* line, + vldoor_e type, + mobj_t* thing ) +{ + player_t* p; + + // only players can open locked doors + p = thing->player; + if (!p) + return 0; + + // check type of linedef, and if key is possessed to open it + switch(line->special) + { + case 99: // Blue Lock + case 133: + if (!p->cards[it_bluecard] && !p->cards[it_blueskull]) + { + p->message = s_PD_BLUEO; // Ty 03/27/98 - externalized + S_StartSound(p->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + + case 134: // Red Lock + case 135: + if (!p->cards[it_redcard] && !p->cards[it_redskull]) + { + p->message = s_PD_REDO; // Ty 03/27/98 - externalized + S_StartSound(p->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + + case 136: // Yellow Lock + case 137: + if (!p->cards[it_yellowcard] && !p->cards[it_yellowskull]) + { + p->message = s_PD_YELLOWO; // Ty 03/27/98 - externalized + S_StartSound(p->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + } + + // got the key, so open the door + return EV_DoDoor(line,type); +} + + +// +// EV_DoDoor +// +// Handle opening a tagged door +// +// Passed the line activating the door and the type of door +// Returns true if a thinker created +// +int EV_DoDoor +( line_t* line, + vldoor_e type ) +{ + int secnum,rtn; + sector_t* sec; + vldoor_t* door; + + secnum = -1; + rtn = 0; + + if (ProcessNoTagLines(line, &sec, &secnum)) {if (zerotag_manual) goto manual_door; else {return rtn;}};//e6y + // open all doors with the same tag as the activating line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; +manual_door://e6y + // if the ceiling already moving, don't start the door action + if (P_SectorActive(ceiling_special,sec)) { //jff 2/22/98 + if (!zerotag_manual) continue; else {return rtn;}}; //e6y + + // new door thinker + rtn = 1; + door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + sec->ceilingdata = door; //jff 2/22/98 + + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->type = type; + door->topwait = VDOORWAIT; + door->speed = VDOORSPEED; + door->line = line; // jff 1/31/98 remember line that triggered us + door->lighttag = 0; /* killough 10/98: no light effects with tagged doors */ + + // setup door parameters according to type of door + switch(type) + { + case blazeClose: + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->direction = -1; + door->speed = VDOORSPEED * 4; + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdcls); + break; + + case closeDoor: + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->direction = -1; + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_dorcls); + break; + + case close30ThenOpen: + door->topheight = sec->ceilingheight; + door->direction = -1; + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_dorcls); + break; + + case blazeRaise: + case blazeOpen: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->speed = VDOORSPEED * 4; + if (door->topheight != sec->ceilingheight) + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdopn); + break; + + case normal: + case openDoor: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + if (door->topheight != sec->ceilingheight) + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_doropn); + break; + + default: + break; + } + if (zerotag_manual) return rtn; //e6y + } + return rtn; +} + + +// +// EV_VerticalDoor +// +// Handle opening a door manually, no tag value +// +// Passed the line activating the door and the thing activating it +// Returns true if a thinker created +// +// jff 2/12/98 added int return value, fixed all returns +// +int EV_VerticalDoor +( line_t* line, + mobj_t* thing ) +{ + player_t* player; + sector_t* sec; + vldoor_t* door; + + // Check for locks + player = thing->player; + + switch(line->special) + { + case 26: // Blue Lock + case 32: + if ( !player ) + return 0; + if (!player->cards[it_bluecard] && !player->cards[it_blueskull]) + { + player->message = s_PD_BLUEK; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + + case 27: // Yellow Lock + case 34: + if ( !player ) + return 0; + if (!player->cards[it_yellowcard] && !player->cards[it_yellowskull]) + { + player->message = s_PD_YELLOWK; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + + case 28: // Red Lock + case 33: + if ( !player ) + return 0; + if (!player->cards[it_redcard] && !player->cards[it_redskull]) + { + player->message = s_PD_REDK; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + + default: + break; + } + + // if the wrong side of door is pushed, give oof sound + if (line->sidenum[1]==NO_INDEX) // killough + { + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return 0; + } + + // get the sector on the second side of activating linedef + sec = sides[line->sidenum[1]].sector; + + /* if door already has a thinker, use it + * cph 2001/04/05 - + * Ok, this is a disaster area. We're assuming that sec->ceilingdata + * is a vldoor_t! What if this door is controlled by both DR lines + * and by switches? I don't know how to fix that. + * Secondly, original Doom didn't distinguish floor/lighting/ceiling + * actions, so we need to do the same in demo compatibility mode. + */ + door = sec->ceilingdata; + if (demo_compatibility) { + if (!door) door = sec->floordata; + if (!door) door = sec->lightingdata; + } + /* If this is a repeatable line, and the door is already moving, then we can just reverse the current action. Note that in prboom 2.3.0 I erroneously removed the if-this-is-repeatable check, hence the prboom_4_compatibility clause below (foolishly assumed that already moving implies repeatable - but it could be moving due to another switch, e.g. lv19-509) */ + if (door && + ((compatibility_level == prboom_4_compatibility) || + (line->special == 1) || (line->special == 117) || (line->special == 26) || (line->special == 27) || (line->special == 28) + ) + ) { + /* For old demos we have to emulate the old buggy behavior and + * mess up non-T_VerticalDoor actions. + */ + if (compatibility_level < prboom_4_compatibility || + door->thinker.function == T_VerticalDoor) { + /* cph - we are writing outval to door->direction iff it is non-zero */ + signed int outval = 0; + + /* An already moving repeatable door which is being re-pressed, or a + * monster is trying to open a closing door - so change direction + * DEMOSYNC: we only read door->direction now if it really is a door. + */ + if (door->thinker.function == T_VerticalDoor && door->direction == -1) { + outval = 1; /* go back up */ + } else if (player) { + outval = -1; /* go back down */ + } + + /* Write this to the thinker. In demo compatibility mode, we might be + * overwriting a field of a non-vldoor_t thinker - we need to add any + * other thinker types here if any demos depend on specific fields + * being corrupted by this. + */ + if (outval) { + if (door->thinker.function == T_VerticalDoor) { + door->direction = outval; + } else if (door->thinker.function == T_PlatRaise) { + plat_t* p = (plat_t*)door; + p->wait = outval; + } else { + lprintf(LO_DEBUG, "EV_VerticalDoor: unknown thinker.function in thinker corruption emulation"); + } + + return 1; + } + } + /* Either we're in prboom >=v2.3 and it's not a door, or it's a door but + * we're a monster and don't want to shut it; exit with no action. + */ + return 0; + } + + // emit proper sound + switch(line->special) + { + case 117: // blazing door raise + case 118: // blazing door open + S_StartSound((mobj_t *)&sec->soundorg,sfx_bdopn); + break; + + default: // normal or locked door sound + S_StartSound((mobj_t *)&sec->soundorg,sfx_doropn); + break; + } + + // new door thinker + door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + sec->ceilingdata = door; //jff 2/22/98 + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->direction = 1; + door->speed = VDOORSPEED; + door->topwait = VDOORWAIT; + door->line = line; // jff 1/31/98 remember line that triggered us + + /* killough 10/98: use gradual lighting changes if nonzero tag given */ + door->lighttag = comp[comp_doorlight] ? 0 : line->tag; + + // set the type of door from the activating linedef type + switch(line->special) + { + case 1: + case 26: + case 27: + case 28: + door->type = normal; + break; + + case 31: + case 32: + case 33: + case 34: + door->type = openDoor; + line->special = 0; + break; + + case 117: // blazing door raise + door->type = blazeRaise; + door->speed = VDOORSPEED*4; + break; + case 118: // blazing door open + door->type = blazeOpen; + line->special = 0; + door->speed = VDOORSPEED*4; + break; + + default: + door->lighttag = 0; // killough 10/98 + break; + } + + // find the top and bottom of the movement range + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + return 1; +} + + +/////////////////////////////////////////////////////////////// +// +// Sector type door spawners +// +/////////////////////////////////////////////////////////////// + +// +// P_SpawnDoorCloseIn30() +// +// Spawn a door that closes after 30 seconds (called at level init) +// +// Passed the sector of the door, whose type specified the door action +// Returns nothing +// +void P_SpawnDoorCloseIn30 (sector_t* sec) +{ + vldoor_t* door; + + door = Z_Malloc ( sizeof(*door), PU_LEVSPEC, 0); + + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + + sec->ceilingdata = door; //jff 2/22/98 + sec->special = 0; + + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->direction = 0; + door->type = normal; + door->speed = VDOORSPEED; + door->topcountdown = 30 * 35; + door->line = NULL; // jff 1/31/98 remember line that triggered us + door->lighttag = 0; /* killough 10/98: no lighting changes */ +} + +// +// P_SpawnDoorRaiseIn5Mins() +// +// Spawn a door that opens after 5 minutes (called at level init) +// +// Passed the sector of the door, whose type specified the door action +// Returns nothing +// +void P_SpawnDoorRaiseIn5Mins +( sector_t* sec, + int secnum ) +{ + vldoor_t* door; + + door = Z_Malloc ( sizeof(*door), PU_LEVSPEC, 0); + + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + + sec->ceilingdata = door; //jff 2/22/98 + sec->special = 0; + + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->direction = 2; + door->type = raiseIn5Mins; + door->speed = VDOORSPEED; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->topwait = VDOORWAIT; + door->topcountdown = 5 * 60 * 35; + door->line = NULL; // jff 1/31/98 remember line that triggered us + door->lighttag = 0; /* killough 10/98: no lighting changes */ +} diff --git a/src/p_enemy.c b/src/p_enemy.c new file mode 100644 index 0000000..d3f92be --- /dev/null +++ b/src/p_enemy.c @@ -0,0 +1,2833 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000,2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Enemy thinking, AI. + * Action Pointer Functions + * that are associated with states/frames. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "m_random.h" +#include "r_main.h" +#include "p_maputl.h" +#include "p_map.h" +#include "p_setup.h" +#include "p_spec.h" +#include "s_sound.h" +#include "sounds.h" +#include "p_inter.h" +#include "g_game.h" +#include "p_enemy.h" +#include "p_tick.h" +#include "i_sound.h" +#include "m_bbox.h" +#include "hu_stuff.h" +#include "lprintf.h" +#include "e6y.h"//e6y + +static mobj_t *current_actor; + +typedef enum { + DI_EAST, + DI_NORTHEAST, + DI_NORTH, + DI_NORTHWEST, + DI_WEST, + DI_SOUTHWEST, + DI_SOUTH, + DI_SOUTHEAST, + DI_NODIR, + NUMDIRS +} dirtype_e; + +typedef int dirtype_t; + +static void P_NewChaseDir(mobj_t *actor); +void P_ZBumpCheck(mobj_t *); // phares + +// +// ENEMY THINKING +// Enemies are allways spawned +// with targetplayer = -1, threshold = 0 +// Most monsters are spawned unaware of all players, +// but some can be made preaware +// + +// +// Called by P_NoiseAlert. +// Recursively traverse adjacent sectors, +// sound blocking lines cut off traversal. +// +// killough 5/5/98: reformatted, cleaned up + +static void P_RecursiveSound(sector_t *sec, int soundblocks, + mobj_t *soundtarget) +{ + int i; + + // 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; + P_SetTarget(&sec->soundtarget, soundtarget); + + for (i=0; ilinecount; i++) + { + sector_t *other; + line_t *check = sec->lines[i]; + + if (!(check->flags & ML_TWOSIDED)) + continue; + + P_LineOpening(check); + + if (openrange <= 0) + continue; // closed door + + other=sides[check->sidenum[sides[check->sidenum[0]].sector==sec]].sector; + + if (!(check->flags & ML_SOUNDBLOCK)) + P_RecursiveSound(other, soundblocks, soundtarget); + else + if (!soundblocks) + P_RecursiveSound(other, 1, soundtarget); + } +} + +// +// P_NoiseAlert +// If a monster yells at a player, +// it will alert other monsters to the player. +// +void P_NoiseAlert(mobj_t *target, mobj_t *emitter) +{ + if (target != NULL && target->player && (target->player->cheats & CF_NOTARGET)) + return; + + validcount++; + P_RecursiveSound(emitter->subsector->sector, 0, target); +} + +// +// P_CheckMeleeRange +// + +static dboolean P_CheckMeleeRange(mobj_t *actor) +{ + mobj_t *pl = actor->target; + + return // killough 7/18/98: friendly monsters don't attack other friends + pl && !(actor->flags & pl->flags & MF_FRIEND) && + (P_AproxDistance(pl->x-actor->x, pl->y-actor->y) < + (compatibility_level == doom_12_compatibility ? + MELEERANGE : + MELEERANGE - 20*FRACUNIT + pl->info->radius)) && + P_CheckSight(actor, actor->target); +} + +// +// P_HitFriend() +// +// killough 12/98 +// This function tries to prevent shooting at friends + +static dboolean P_HitFriend(mobj_t *actor) +{ + return actor->flags & MF_FRIEND && actor->target && + (P_AimLineAttack(actor, + R_PointToAngle2(actor->x, actor->y, + actor->target->x, actor->target->y), + P_AproxDistance(actor->x-actor->target->x, + actor->y-actor->target->y), 0), + linetarget) && linetarget != actor->target && + !((linetarget->flags ^ actor->flags) & MF_FRIEND); +} + +// +// P_CheckMissileRange +// +static dboolean P_CheckMissileRange(mobj_t *actor) +{ + fixed_t dist; + + if (!P_CheckSight(actor, actor->target)) + return false; + + if (actor->flags & MF_JUSTHIT) + { // the target just hit the enemy, so fight back! + actor->flags &= ~MF_JUSTHIT; + + /* killough 7/18/98: no friendly fire at corpses + * killough 11/98: prevent too much infighting among friends + * cph - yikes, talk about fitting everything on one line... */ + + return + !(actor->flags & MF_FRIEND) || + (actor->target->health > 0 && + (!(actor->target->flags & MF_FRIEND) || + (actor->target->player ? + monster_infighting || P_Random(pr_defect) >128 : + !(actor->target->flags & MF_JUSTHIT) && P_Random(pr_defect) >128))); + } + + /* killough 7/18/98: friendly monsters don't attack other friendly + * monsters or players (except when attacked, and then only once) + */ + if (actor->flags & actor->target->flags & MF_FRIEND) + return false; + + if (actor->reactiontime) + return false; // do not attack yet + + // OPTIMIZE: get this from a global checksight + dist = P_AproxDistance ( actor->x-actor->target->x, + actor->y-actor->target->y) - 64*FRACUNIT; + + if (!actor->info->meleestate) + dist -= 128*FRACUNIT; // no melee attack, so fire more + + dist >>= 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(pr_missrange) < dist) + return false; + + if (P_HitFriend(actor)) + return false; + + return true; +} + +/* + * P_IsOnLift + * + * killough 9/9/98: + * + * Returns true if the object is on a lift. Used for AI, + * since it may indicate the need for crowded conditions, + * or that a monster should stay on the lift for a while + * while it goes up or down. + */ + +static dboolean P_IsOnLift(const mobj_t *actor) +{ + const sector_t *sec = actor->subsector->sector; + line_t line; + int l; + + // Short-circuit: it's on a lift which is active. + if (sec->floordata && ((thinker_t *) sec->floordata)->function==T_PlatRaise) + return true; + + // Check to see if it's in a sector which can be activated as a lift. + if ((line.tag = sec->tag)) + for (l = -1; (l = P_FindLineFromLineTag(&line, l)) >= 0;) + switch (lines[l].special) + { + case 10: case 14: case 15: case 20: case 21: case 22: + case 47: case 53: case 62: case 66: case 67: case 68: + case 87: case 88: case 95: case 120: case 121: case 122: + case 123: case 143: case 162: case 163: case 181: case 182: + case 144: case 148: case 149: case 211: case 227: case 228: + case 231: case 232: case 235: case 236: + return true; + } + + return false; +} + +/* + * P_IsUnderDamage + * + * killough 9/9/98: + * + * Returns nonzero if the object is under damage based on + * their current position. Returns 1 if the damage is moderate, + * -1 if it is serious. Used for AI. + */ + +static int P_IsUnderDamage(mobj_t *actor) +{ + const struct msecnode_s *seclist; + const ceiling_t *cl; // Crushing ceiling + int dir = 0; + for (seclist=actor->touching_sectorlist; seclist; seclist=seclist->m_tnext) + if ((cl = seclist->m_sector->ceilingdata) && + cl->thinker.function == T_MoveCeiling) + dir |= cl->direction; + return dir; +} + +// +// P_Move +// Move in the current direction, +// returns false if the move is blocked. +// + +static fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000}; +static fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000}; + +// 1/11/98 killough: Limit removed on special lines crossed +extern line_t **spechit; // New code -- killough +extern int numspechit; + +static dboolean P_Move(mobj_t *actor, dboolean dropoff) /* killough 9/12/98 */ +{ + fixed_t tryx, tryy, deltax, deltay, origx, origy; + dboolean try_ok; + int movefactor = ORIG_FRICTION_FACTOR; // killough 10/98 + int friction = ORIG_FRICTION; + int speed; + + if (actor->movedir == DI_NODIR) + return false; + +#ifdef RANGECHECK + if ((unsigned)actor->movedir >= 8) + I_Error ("P_Move: Weird actor->movedir!"); +#endif + + // killough 10/98: make monsters get affected by ice and sludge too: + + if (monster_friction) + movefactor = P_GetMoveFactor(actor, &friction); + + speed = actor->info->speed; + + if (friction < ORIG_FRICTION && // sludge + !(speed = ((ORIG_FRICTION_FACTOR - (ORIG_FRICTION_FACTOR-movefactor)/2) + * speed) / ORIG_FRICTION_FACTOR)) + speed = 1; // always give the monster a little bit of speed + + tryx = (origx = actor->x) + (deltax = speed * xspeed[actor->movedir]); + tryy = (origy = actor->y) + (deltay = speed * yspeed[actor->movedir]); + + try_ok = P_TryMove(actor, tryx, tryy, dropoff); + + // killough 10/98: + // Let normal momentum carry them, instead of steptoeing them across ice. + + if (try_ok && friction > ORIG_FRICTION) + { + // COMPAT: MBF calls P_UnsetThingPosition()/P_SetThingPosition() and + // restores floorz, ceilingz and dropoffz values as well + actor->x = origx; + actor->y = origy; + movefactor *= FRACUNIT / ORIG_FRICTION_FACTOR / 4; + actor->momx += FixedMul(deltax, movefactor); + actor->momy += FixedMul(deltay, movefactor); + } + + if (!try_ok) + { // open any specials + int good; + + if (actor->flags & MF_FLOAT && floatok) + { + if (actor->z < tmfloorz) // must adjust height + actor->z += FLOATSPEED; + else + actor->z -= FLOATSPEED; + + actor->flags |= MF_INFLOAT; + + return true; + } + + if (!numspechit) + return false; + + actor->movedir = DI_NODIR; + + /* if the special is not a door that can be opened, return false + * + * killough 8/9/98: this is what caused monsters to get stuck in + * doortracks, because it thought that the monster freed itself + * by opening a door, even if it was moving towards the doortrack, + * and not the door itself. + * + * killough 9/9/98: If a line blocking the monster is activated, + * return true 90% of the time. If a line blocking the monster is + * not activated, but some other line is, return false 90% of the + * time. A bit of randomness is needed to ensure it's free from + * lockups, but for most cases, it returns the correct result. + * + * Do NOT simply return false 1/4th of the time (causes monsters to + * back out when they shouldn't, and creates secondary stickiness). + */ + + for (good = false; numspechit--; ) + if (P_UseSpecialLine(actor, spechit[numspechit], 0, false)) + good |= spechit[numspechit] == blockline ? 1 : 2; + + /* cph - compatibility maze here + * Boom v2.01 and orig. Doom return "good" + * Boom v2.02 and LxDoom return good && (P_Random(pr_trywalk)&3) + * MBF plays even more games + */ + if (!good || comp[comp_doorstuck]) return good; + if (!mbf_features) + return (P_Random(pr_trywalk)&3); /* jff 8/13/98 */ + else /* finally, MBF code */ + return ((P_Random(pr_opendoor) >= 230) ^ (good & 1)); + } + else + actor->flags &= ~MF_INFLOAT; + + /* killough 11/98: fall more slowly, under gravity, if felldown==true */ + if (!(actor->flags & MF_FLOAT) && + (!felldown || !mbf_features)) + actor->z = actor->floorz; + + return true; +} + +/* + * P_SmartMove + * + * killough 9/12/98: Same as P_Move, except smarter + */ + +static dboolean P_SmartMove(mobj_t *actor) +{ + mobj_t *target = actor->target; + int on_lift, dropoff = false, under_damage; + int tmp_monster_avoid_hazards = (prboom_comp[PC_MONSTER_AVOID_HAZARDS].state ? + true : (demo_compatibility ? false : monster_avoid_hazards));//e6y + + /* killough 9/12/98: Stay on a lift if target is on one */ + on_lift = !comp[comp_staylift] + && target && target->health > 0 + && target->subsector->sector->tag==actor->subsector->sector->tag && + P_IsOnLift(actor); + + under_damage = tmp_monster_avoid_hazards && P_IsUnderDamage(actor);//e6y + + // killough 10/98: allow dogs to drop off of taller ledges sometimes. + // dropoff==1 means always allow it, dropoff==2 means only up to 128 high, + // and only if the target is immediately on the other side of the line. + + // haleyjd: allow all friends of HelperType to also jump down + + if ((actor->type == MT_DOGS || (actor->type == (HelperThing-1) && actor->flags&MF_FRIEND)) + && target && dog_jumping && + !((target->flags ^ actor->flags) & MF_FRIEND) && + P_AproxDistance(actor->x - target->x, + actor->y - target->y) < FRACUNIT*144 && + P_Random(pr_dropoff) < 235) + dropoff = 2; + + if (!P_Move(actor, dropoff)) + return false; + + // killough 9/9/98: avoid crushing ceilings or other damaging areas + if ( + (on_lift && P_Random(pr_stayonlift) < 230 && // Stay on lift + !P_IsOnLift(actor)) + || + (tmp_monster_avoid_hazards && !under_damage &&//e6y // Get away from damage + (under_damage = P_IsUnderDamage(actor)) && + (under_damage < 0 || P_Random(pr_avoidcrush) < 200)) + ) + actor->movedir = DI_NODIR; // avoid the area (most of the time anyway) + + return true; +} + +// +// TryWalk +// Attempts to move actor on +// in its current (ob->moveangle) direction. +// If blocked by either a wall or an actor +// returns FALSE +// If move is either clear or blocked only by a door, +// returns TRUE and sets... +// If a door is in the way, +// an OpenDoor call is made to start it opening. +// + +static dboolean P_TryWalk(mobj_t *actor) +{ + if (!P_SmartMove(actor)) + return false; + actor->movecount = P_Random(pr_trywalk)&15; + return true; +} + +// +// P_DoNewChaseDir +// +// killough 9/8/98: +// +// Most of P_NewChaseDir(), except for what +// determines the new direction to take +// + +static void P_DoNewChaseDir(mobj_t *actor, fixed_t deltax, fixed_t deltay) +{ + dirtype_t xdir, ydir, tdir; + dirtype_t olddir = actor->movedir; + dirtype_t turnaround = olddir; + + if (turnaround != DI_NODIR) // find reverse direction + turnaround ^= 4; + + xdir = + deltax > 10*FRACUNIT ? DI_EAST : + deltax < -10*FRACUNIT ? DI_WEST : DI_NODIR; + + ydir = + deltay < -10*FRACUNIT ? DI_SOUTH : + deltay > 10*FRACUNIT ? DI_NORTH : DI_NODIR; + + // try direct route + if (xdir != DI_NODIR && ydir != DI_NODIR && turnaround != + (actor->movedir = deltay < 0 ? deltax > 0 ? DI_SOUTHEAST : DI_SOUTHWEST : + deltax > 0 ? DI_NORTHEAST : DI_NORTHWEST) && P_TryWalk(actor)) + return; + + // try other directions + if (P_Random(pr_newchase) > 200 || D_abs(deltay)>D_abs(deltax)) + tdir = xdir, xdir = ydir, ydir = tdir; + + if ((xdir == turnaround ? xdir = DI_NODIR : xdir) != DI_NODIR && + (actor->movedir = xdir, P_TryWalk(actor))) + return; // either moved forward or attacked + + if ((ydir == turnaround ? ydir = DI_NODIR : ydir) != DI_NODIR && + (actor->movedir = ydir, P_TryWalk(actor))) + return; + + // there is no direct path to the player, so pick another direction. + if (olddir != DI_NODIR && (actor->movedir = olddir, P_TryWalk(actor))) + return; + + // randomly determine direction of search + if (P_Random(pr_newchasedir) & 1) + { + for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++) + if (tdir != turnaround && (actor->movedir = tdir, P_TryWalk(actor))) + return; + } + else + for (tdir = DI_SOUTHEAST; tdir != DI_EAST-1; tdir--) + if (tdir != turnaround && (actor->movedir = tdir, P_TryWalk(actor))) + return; + + if ((actor->movedir = turnaround) != DI_NODIR && !P_TryWalk(actor)) + actor->movedir = DI_NODIR; +} + +// +// killough 11/98: +// +// Monsters try to move away from tall dropoffs. +// +// In Doom, they were never allowed to hang over dropoffs, +// and would remain stuck if involuntarily forced over one. +// This logic, combined with p_map.c (P_TryMove), allows +// monsters to free themselves without making them tend to +// hang over dropoffs. + +static fixed_t dropoff_deltax, dropoff_deltay, floorz; + +static dboolean PIT_AvoidDropoff(line_t *line) +{ + if (line->backsector && // Ignore one-sided linedefs + tmbbox[BOXRIGHT] > line->bbox[BOXLEFT] && + tmbbox[BOXLEFT] < line->bbox[BOXRIGHT] && + tmbbox[BOXTOP] > line->bbox[BOXBOTTOM] && // Linedef must be contacted + tmbbox[BOXBOTTOM] < line->bbox[BOXTOP] && + P_BoxOnLineSide(tmbbox, line) == -1) + { + fixed_t front = line->frontsector->floorheight; + fixed_t back = line->backsector->floorheight; + angle_t angle; + + // The monster must contact one of the two floors, + // and the other must be a tall dropoff (more than 24). + + if (back == floorz && front < floorz - FRACUNIT*24) + angle = R_PointToAngle2(0,0,line->dx,line->dy); // front side dropoff + else + if (front == floorz && back < floorz - FRACUNIT*24) + angle = R_PointToAngle2(line->dx,line->dy,0,0); // back side dropoff + else + return true; + + // Move away from dropoff at a standard speed. + // Multiple contacted linedefs are cumulative (e.g. hanging over corner) + dropoff_deltax -= finesine[angle >> ANGLETOFINESHIFT]*32; + dropoff_deltay += finecosine[angle >> ANGLETOFINESHIFT]*32; + } + return true; +} + +// +// Driver for above +// + +static fixed_t P_AvoidDropoff(mobj_t *actor) +{ + int yh=P_GetSafeBlockY((tmbbox[BOXTOP] = actor->y+actor->radius)-bmaporgy); + int yl=P_GetSafeBlockY((tmbbox[BOXBOTTOM]= actor->y-actor->radius)-bmaporgy); + int xh=P_GetSafeBlockX((tmbbox[BOXRIGHT] = actor->x+actor->radius)-bmaporgx); + int xl=P_GetSafeBlockX((tmbbox[BOXLEFT] = actor->x-actor->radius)-bmaporgx); + int bx, by; + + floorz = actor->z; // remember floor height + + dropoff_deltax = dropoff_deltay = 0; + + // check lines + + validcount++; + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + P_BlockLinesIterator(bx, by, PIT_AvoidDropoff); // all contacted lines + + return dropoff_deltax | dropoff_deltay; // Non-zero if movement prescribed +} + +// +// P_NewChaseDir +// +// killough 9/8/98: Split into two functions +// + +static void P_NewChaseDir(mobj_t *actor) +{ + mobj_t *target = actor->target; + fixed_t deltax = target->x - actor->x; + fixed_t deltay = target->y - actor->y; + + // killough 8/8/98: sometimes move away from target, keeping distance + // + // 1) Stay a certain distance away from a friend, to avoid being in their way + // 2) Take advantage over an enemy without missiles, by keeping distance + + actor->strafecount = 0; + + if (mbf_features) { + if (actor->floorz - actor->dropoffz > FRACUNIT*24 && + actor->z <= actor->floorz && + !(actor->flags & (MF_DROPOFF|MF_FLOAT)) && + !comp[comp_dropoff] && + P_AvoidDropoff(actor)) /* Move away from dropoff */ + { + P_DoNewChaseDir(actor, dropoff_deltax, dropoff_deltay); + + // If moving away from dropoff, set movecount to 1 so that + // small steps are taken to get monster away from dropoff. + + actor->movecount = 1; + return; + } + else + { + fixed_t dist = P_AproxDistance(deltax, deltay); + + // Move away from friends when too close, except + // in certain situations (e.g. a crowded lift) + + if (actor->flags & target->flags & MF_FRIEND && + distfriend << FRACBITS > dist && + !P_IsOnLift(target) && !P_IsUnderDamage(actor)) + { + deltax = -deltax, deltay = -deltay; + } else + if (target->health > 0 && (actor->flags ^ target->flags) & MF_FRIEND) + { // Live enemy target + if (monster_backing && + actor->info->missilestate && actor->type != MT_SKULL && + ((!target->info->missilestate && dist < MELEERANGE*2) || + (target->player && dist < MELEERANGE*3 && + (target->player->readyweapon == wp_fist || + target->player->readyweapon == wp_chainsaw)))) + { // Back away from melee attacker + actor->strafecount = P_Random(pr_enemystrafe) & 15; + deltax = -deltax, deltay = -deltay; + } + } + } + } + + P_DoNewChaseDir(actor, deltax, deltay); + + // If strafing, set movecount to strafecount so that old Doom + // logic still works the same, except in the strafing part + + if (actor->strafecount) + actor->movecount = actor->strafecount; +} + +// +// P_IsVisible +// +// killough 9/9/98: whether a target is visible to a monster +// + +static dboolean P_IsVisible(mobj_t *actor, mobj_t *mo, dboolean allaround) +{ + if (!allaround) + { + angle_t an = R_PointToAngle2(actor->x, actor->y, + mo->x, mo->y) - actor->angle; + if (an > ANG90 && an < ANG270 && + P_AproxDistance(mo->x-actor->x, mo->y-actor->y) > MELEERANGE) + return false; + } + return P_CheckSight(actor, mo); +} + +// +// PIT_FindTarget +// +// killough 9/5/98 +// +// Finds monster targets for other monsters +// + +static int current_allaround; + +static dboolean PIT_FindTarget(mobj_t *mo) +{ + mobj_t *actor = current_actor; + + if (!((mo->flags ^ actor->flags) & MF_FRIEND && // Invalid target + mo->health > 0 && (mo->flags & MF_COUNTKILL || mo->type == MT_SKULL))) + return true; + + // If the monster is already engaged in a one-on-one attack + // with a healthy friend, don't attack around 60% the time + { + const mobj_t *targ = mo->target; + if (targ && targ->target == mo && + P_Random(pr_skiptarget) > 100 && + (targ->flags ^ mo->flags) & MF_FRIEND && + targ->health*2 >= targ->info->spawnhealth) + return true; + } + + if (!P_IsVisible(actor, mo, current_allaround)) + return true; + + P_SetTarget(&actor->lastenemy, actor->target); // Remember previous target + P_SetTarget(&actor->target, mo); // Found target + + // Move the selected monster to the end of its associated + // list, so that it gets searched last next time. + + { + thinker_t *cap = &thinkerclasscap[mo->flags & MF_FRIEND ? + th_friends : th_enemies]; + (mo->thinker.cprev->cnext = mo->thinker.cnext)->cprev = mo->thinker.cprev; + (mo->thinker.cprev = cap->cprev)->cnext = &mo->thinker; + (mo->thinker.cnext = cap)->cprev = &mo->thinker; + } + + return false; +} + +// +// P_LookForPlayers +// If allaround is false, only look 180 degrees in front. +// Returns true if a player is targeted. +// + +static dboolean P_LookForPlayers(mobj_t *actor, dboolean allaround) +{ + player_t *player; + int stop, stopc, c; + + if (actor->flags & MF_FRIEND) + { // killough 9/9/98: friendly monsters go about players differently + int anyone; + +#if 0 + if (!allaround) // If you want friendly monsters not to awaken unprovoked + return false; +#endif + + // Go back to a player, no matter whether it's visible or not + for (anyone=0; anyone<=1; anyone++) + for (c=0; ctarget, players[c].mo); + + // killough 12/98: + // get out of refiring loop, to avoid hitting player accidentally + + if (actor->info->missilestate) + { + P_SetMobjState(actor, actor->info->seestate); + actor->flags &= ~MF_JUSTHIT; + } + + return true; + } + + return false; + } + + // Change mask of 3 to (MAXPLAYERS-1) -- killough 2/15/98: + stop = (actor->lastlook-1)&(MAXPLAYERS-1); + + c = 0; + + stopc = !mbf_features && + !demo_compatibility && monsters_remember ? + MAXPLAYERS : 2; // killough 9/9/98 + + for (;; actor->lastlook = (actor->lastlook+1)&(MAXPLAYERS-1)) + { + if (!playeringame[actor->lastlook]) + continue; + + // killough 2/15/98, 9/9/98: + if (c++ == stopc || actor->lastlook == stop) // done looking + { + // e6y + // Fixed Boom incompatibilities. The following code was missed. + // There are no more desyncs on Donce's demos on horror.wad + + // Use last known enemy if no players sighted -- killough 2/15/98: + if (!mbf_features && !demo_compatibility && monsters_remember) + { + if (actor->lastenemy && actor->lastenemy->health > 0) + { + actor->target = actor->lastenemy; + actor->lastenemy = NULL; + return true; + } + } + + return false; + } + + player = &players[actor->lastlook]; + + if (player->cheats & CF_NOTARGET) + continue; // no target + + if (player->health <= 0) + continue; // dead + + if (!P_IsVisible(actor, player->mo, allaround)) + continue; + + P_SetTarget(&actor->target, player->mo); + + /* killough 9/9/98: give monsters a threshold towards getting players + * (we don't want it to be too easy for a player with dogs :) + */ + if (!comp[comp_pursuit]) + actor->threshold = 60; + + return true; + } +} + +// +// Friendly monsters, by Lee Killough 7/18/98 +// +// Friendly monsters go after other monsters first, but +// also return to owner if they cannot find any targets. +// A marine's best friend :) killough 7/18/98, 9/98 +// + +static dboolean P_LookForMonsters(mobj_t *actor, dboolean allaround) +{ + thinker_t *cap, *th; + + if (demo_compatibility) + return false; + + if (actor->lastenemy && actor->lastenemy->health > 0 && monsters_remember && + !(actor->lastenemy->flags & actor->flags & MF_FRIEND)) // not friends + { + P_SetTarget(&actor->target, actor->lastenemy); + P_SetTarget(&actor->lastenemy, NULL); + return true; + } + + /* Old demos do not support monster-seeking bots */ + if (!mbf_features) + return false; + + // Search the threaded list corresponding to this object's potential targets + cap = &thinkerclasscap[actor->flags & MF_FRIEND ? th_enemies : th_friends]; + + // Search for new enemy + + if (cap->cnext != cap) // Empty list? bail out early + { + int x = P_GetSafeBlockX(actor->x - bmaporgx); + int y = P_GetSafeBlockY(actor->y - bmaporgy); + int d; + + current_actor = actor; + current_allaround = allaround; + + // Search first in the immediate vicinity. + + if (!P_BlockThingsIterator(x, y, PIT_FindTarget)) + return true; + + for (d=1; d<5; d++) + { + int i = 1 - d; + do + if (!P_BlockThingsIterator(x+i, y-d, PIT_FindTarget) || + !P_BlockThingsIterator(x+i, y+d, PIT_FindTarget)) + return true; + while (++i < d); + do + if (!P_BlockThingsIterator(x-d, y+i, PIT_FindTarget) || + !P_BlockThingsIterator(x+d, y+i, PIT_FindTarget)) + return true; + while (--i + d >= 0); + } + + { // Random number of monsters, to prevent patterns from forming + int n = (P_Random(pr_friends) & 31) + 15; + + for (th = cap->cnext; th != cap; th = th->cnext) + if (--n < 0) + { + // Only a subset of the monsters were searched. Move all of + // the ones which were searched so far, to the end of the list. + + (cap->cnext->cprev = cap->cprev)->cnext = cap->cnext; + (cap->cprev = th->cprev)->cnext = cap; + (th->cprev = cap)->cnext = th; + break; + } + else + if (!PIT_FindTarget((mobj_t *) th)) // If target sighted + return true; + } + } + + return false; // No monster found +} + +// +// P_LookForTargets +// +// killough 9/5/98: look for targets to go after, depending on kind of monster +// + +static dboolean P_LookForTargets(mobj_t *actor, int allaround) +{ + return actor->flags & MF_FRIEND ? + P_LookForMonsters(actor, allaround) || P_LookForPlayers (actor, allaround): + P_LookForPlayers (actor, allaround) || P_LookForMonsters(actor, allaround); +} + +// +// P_HelpFriend +// +// killough 9/8/98: Help friends in danger of dying +// + +static dboolean P_HelpFriend(mobj_t *actor) +{ + thinker_t *cap, *th; + + // If less than 33% health, self-preservation rules + if (actor->health*3 < actor->info->spawnhealth) + return false; + + current_actor = actor; + current_allaround = true; + + // Possibly help a friend under 50% health + cap = &thinkerclasscap[actor->flags & MF_FRIEND ? th_friends : th_enemies]; + + for (th = cap->cnext; th != cap; th = th->cnext) + if (((mobj_t *) th)->health*2 >= ((mobj_t *) th)->info->spawnhealth) + { + if (P_Random(pr_helpfriend) < 180) + break; + } + else + if (((mobj_t *) th)->flags & MF_JUSTHIT && + ((mobj_t *) th)->target && + ((mobj_t *) th)->target != actor->target && + !PIT_FindTarget(((mobj_t *) th)->target)) + { + // Ignore any attacking monsters, while searching for friend + actor->threshold = BASETHRESHOLD; + return true; + } + + return false; +} + +// +// A_KeenDie +// DOOM II special, map 32. +// Uses special tag 666. +// +void A_KeenDie(mobj_t* mo) +{ + thinker_t *th; + 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 == P_MobjThinker) + { + mobj_t *mo2 = (mobj_t *) th; + if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) + return; // other Keen not dead + } + + junk.tag = 666; + EV_DoDoor(&junk,openDoor); +} + + +// +// ACTION ROUTINES +// + +// +// A_Look +// Stay in state until a player is sighted. +// + +void A_Look(mobj_t *actor) +{ + mobj_t *targ = actor->subsector->sector->soundtarget; + actor->threshold = 0; // any shot will wake up + + if (targ && targ->player && (targ->player->cheats & CF_NOTARGET)) + return; + + /* killough 7/18/98: + * Friendly monsters go after other monsters first, but + * also return to player, without attacking them, if they + * cannot find any targets. A marine's best friend :) + */ + actor->pursuecount = 0; + + if (!(actor->flags & MF_FRIEND && P_LookForTargets(actor, false)) && + !((targ = actor->subsector->sector->soundtarget) && + targ->flags & MF_SHOOTABLE && + (P_SetTarget(&actor->target, targ), + !(actor->flags & MF_AMBUSH) || P_CheckSight(actor, targ))) && + (actor->flags & MF_FRIEND || !P_LookForTargets(actor, false))) + return; + + // go into chase state + + if (actor->info->seesound) + { + int sound; + switch (actor->info->seesound) + { + case sfx_posit1: + case sfx_posit2: + case sfx_posit3: + sound = sfx_posit1+P_Random(pr_see)%3; + break; + + case sfx_bgsit1: + case sfx_bgsit2: + sound = sfx_bgsit1+P_Random(pr_see)%2; + break; + + default: + sound = actor->info->seesound; + break; + } + if (actor->type==MT_SPIDER || actor->type == MT_CYBORG) + S_StartSound(NULL, sound); // full volume + else + { + S_StartSound(actor, sound); + // [FG] make seesounds uninterruptible + if (full_sounds) + S_UnlinkSound(actor); + } + } + P_SetMobjState(actor, actor->info->seestate); +} + +// +// A_KeepChasing +// +// killough 10/98: +// Allows monsters to continue movement while attacking +// + +static void A_KeepChasing(mobj_t *actor) +{ + if (actor->movecount) + { + actor->movecount--; + if (actor->strafecount) + actor->strafecount--; + P_SmartMove(actor); + } +} + +// +// A_Chase +// Actor has a melee attack, +// so it tries to close as fast as possible +// + +void A_Chase(mobj_t *actor) +{ + if (actor->reactiontime) + actor->reactiontime--; + + if (actor->threshold) { /* modify target threshold */ + if (compatibility_level == doom_12_compatibility) + { + actor->threshold--; + } + else + { + if (!actor->target || actor->target->health <= 0) + actor->threshold = 0; + else + actor->threshold--; + } + } + + /* turn towards movement direction if not there yet + * killough 9/7/98: keep facing towards target if strafing or backing out + */ + + if (actor->strafecount) + A_FaceTarget(actor); + else if (actor->movedir < 8) + { + int delta = (actor->angle &= (7<<29)) - (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)) + { + if (!P_LookForTargets(actor,true)) // look for a new target + P_SetMobjState(actor, actor->info->spawnstate); // no new target + return; + } + + // do not attack twice in a row + if (actor->flags & MF_JUSTATTACKED) + { + actor->flags &= ~MF_JUSTATTACKED; + if (gameskill != sk_nightmare && !fastparm) + P_NewChaseDir(actor); + return; + } + + // check for melee attack + if (actor->info->meleestate && P_CheckMeleeRange(actor)) + { + if (actor->info->attacksound) + S_StartSound(actor, actor->info->attacksound); + P_SetMobjState(actor, actor->info->meleestate); + /* killough 8/98: remember an attack + * cph - DEMOSYNC? */ + if (!actor->info->missilestate) + actor->flags |= MF_JUSTHIT; + return; + } + + // check for missile attack + if (actor->info->missilestate) + if (!(gameskill < sk_nightmare && !fastparm && actor->movecount)) + if (P_CheckMissileRange(actor)) + { + P_SetMobjState(actor, actor->info->missilestate); + actor->flags |= MF_JUSTATTACKED; + return; + } + + if (!actor->threshold) { + if (!mbf_features) + { /* killough 9/9/98: for backward demo compatibility */ + if (netgame && !P_CheckSight(actor, actor->target) && + P_LookForPlayers(actor, true)) + return; + } + /* killough 7/18/98, 9/9/98: new monster AI */ + else if (help_friends && P_HelpFriend(actor)) + return; /* killough 9/8/98: Help friends in need */ + /* Look for new targets if current one is bad or is out of view */ + else if (actor->pursuecount) + actor->pursuecount--; + else { + /* Our pursuit time has expired. We're going to think about + * changing targets */ + actor->pursuecount = BASETHRESHOLD; + + /* Unless (we have a live target + * and it's not friendly + * and we can see it) + * try to find a new one; return if sucessful */ + + if (!(actor->target && actor->target->health > 0 && + ((comp[comp_pursuit] && !netgame) || + (((actor->target->flags ^ actor->flags) & MF_FRIEND || + (!(actor->flags & MF_FRIEND) && monster_infighting)) && + P_CheckSight(actor, actor->target)))) + && P_LookForTargets(actor, true)) + return; + + /* (Current target was good, or no new target was found.) + * + * If monster is a missile-less friend, give up pursuit and + * return to player, if no attacks have occurred recently. + */ + + if (!actor->info->missilestate && actor->flags & MF_FRIEND) { + if (actor->flags & MF_JUSTHIT) /* if recent action, */ + actor->flags &= ~MF_JUSTHIT; /* keep fighting */ + else if (P_LookForPlayers(actor, true)) /* else return to player */ + return; + } + } + } + + if (actor->strafecount) + actor->strafecount--; + + // chase towards player + if (--actor->movecount<0 || !P_SmartMove(actor)) + P_NewChaseDir(actor); + + // make active sound + if (actor->info->activesound && P_Random(pr_see)<3) + S_StartSound(actor, actor->info->activesound); +} + +// +// A_FaceTarget +// +void A_FaceTarget(mobj_t *actor) +{ + if (!actor->target) + return; + actor->flags &= ~MF_AMBUSH; + actor->angle = R_PointToAngle2(actor->x, actor->y, + actor->target->x, actor->target->y); + if (actor->target->flags & MF_SHADOW) + { // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_facetarget); + actor->angle += (t-P_Random(pr_facetarget))<<21; + } +} + +// +// A_PosAttack +// + +void A_PosAttack(mobj_t *actor) +{ + int angle, damage, slope, t; + + if (!actor->target) + return; + A_FaceTarget(actor); + angle = actor->angle; + slope = P_AimLineAttack(actor, angle, MISSILERANGE, 0); /* killough 8/2/98 */ + S_StartSound(actor, sfx_pistol); + + // killough 5/5/98: remove dependence on order of evaluation: + t = P_Random(pr_posattack); + angle += (t - P_Random(pr_posattack))<<20; + damage = (P_Random(pr_posattack)%5 + 1)*3; + P_LineAttack(actor, angle, MISSILERANGE, slope, damage); +} + +void A_SPosAttack(mobj_t* actor) +{ + int i, bangle, slope; + + if (!actor->target) + return; + S_StartSound(actor, sfx_shotgn); + A_FaceTarget(actor); + bangle = actor->angle; + slope = P_AimLineAttack(actor, bangle, MISSILERANGE, 0); /* killough 8/2/98 */ + for (i=0; i<3; i++) + { // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_sposattack); + int angle = bangle + ((t - P_Random(pr_sposattack))<<20); + int damage = ((P_Random(pr_sposattack)%5)+1)*3; + P_LineAttack(actor, angle, MISSILERANGE, slope, damage); + } +} + +void A_CPosAttack(mobj_t *actor) +{ + int angle, bangle, damage, slope, t; + + if (!actor->target) + return; + S_StartSound(actor, sfx_shotgn); + A_FaceTarget(actor); + bangle = actor->angle; + slope = P_AimLineAttack(actor, bangle, MISSILERANGE, 0); /* killough 8/2/98 */ + + // killough 5/5/98: remove dependence on order of evaluation: + t = P_Random(pr_cposattack); + angle = bangle + ((t - P_Random(pr_cposattack))<<20); + damage = ((P_Random(pr_cposattack)%5)+1)*3; + P_LineAttack(actor, angle, MISSILERANGE, slope, damage); +} + +void A_CPosRefire(mobj_t *actor) +{ + // keep firing unless target got out of sight + A_FaceTarget(actor); + + /* killough 12/98: Stop firing if a friend has gotten in the way */ + if (P_HitFriend(actor)) + goto stop; + + /* killough 11/98: prevent refiring on friends continuously */ + if (P_Random(pr_cposrefire) < 40) { + if (actor->target && actor->flags & actor->target->flags & MF_FRIEND) + goto stop; + else + return; + } + + if (!actor->target || actor->target->health <= 0 + || !P_CheckSight(actor, actor->target)) +stop: P_SetMobjState(actor, actor->info->seestate); +} + +void A_SpidRefire(mobj_t* actor) +{ + // keep firing unless target got out of sight + A_FaceTarget(actor); + + /* killough 12/98: Stop firing if a friend has gotten in the way */ + if (P_HitFriend(actor)) + goto stop; + + if (P_Random(pr_spidrefire) < 10) + return; + + // killough 11/98: prevent refiring on friends continuously + if (!actor->target || actor->target->health <= 0 + || actor->flags & actor->target->flags & MF_FRIEND + || !P_CheckSight(actor, actor->target)) + stop: P_SetMobjState(actor, actor->info->seestate); +} + +void A_BspiAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + P_SpawnMissile(actor, actor->target, MT_ARACHPLAZ); // launch a missile +} + +// +// A_TroopAttack +// + +void A_TroopAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + if (P_CheckMeleeRange(actor)) + { + int damage; + S_StartSound(actor, sfx_claw); + damage = (P_Random(pr_troopattack)%8+1)*3; + P_DamageMobj(actor->target, actor, actor, damage); + return; + } + P_SpawnMissile(actor, actor->target, MT_TROOPSHOT); // launch a missile +} + +void A_SargAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + if (compatibility_level == doom_12_compatibility) + { + int damage = ((P_Random(pr_sargattack)%10)+1)*4; + P_LineAttack(actor, actor->angle, MELEERANGE, 0, damage); + } + else + { + if (P_CheckMeleeRange(actor)) + { + int damage = ((P_Random(pr_sargattack)%10)+1)*4; + P_DamageMobj(actor->target, actor, actor, damage); + } + } +} + +void A_HeadAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget (actor); + if (P_CheckMeleeRange(actor)) + { + int damage = (P_Random(pr_headattack)%6+1)*10; + P_DamageMobj(actor->target, actor, actor, damage); + return; + } + P_SpawnMissile(actor, actor->target, MT_HEADSHOT); // launch a missile +} + +void A_CyberAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + P_SpawnMissile(actor, actor->target, MT_ROCKET); +} + +void A_BruisAttack(mobj_t *actor) +{ + if (!actor->target) + return; + if (P_CheckMeleeRange(actor)) + { + int damage; + S_StartSound(actor, sfx_claw); + damage = (P_Random(pr_bruisattack)%8+1)*10; + P_DamageMobj(actor->target, actor, actor, damage); + return; + } + P_SpawnMissile(actor, actor->target, MT_BRUISERSHOT); // launch a missile +} + +// +// A_SkelMissile +// + +void A_SkelMissile(mobj_t *actor) +{ + mobj_t *mo; + + if (!actor->target) + return; + + A_FaceTarget (actor); + actor->z += 16*FRACUNIT; // so missile spawns higher + mo = P_SpawnMissile (actor, actor->target, MT_TRACER); + actor->z -= 16*FRACUNIT; // back to normal + + mo->x += mo->momx; + mo->y += mo->momy; + P_SetTarget(&mo->tracer, actor->target); +} + +int TRACEANGLE = 0xc000000; + +void A_Tracer(mobj_t *actor) +{ + angle_t exact; + fixed_t dist; + fixed_t slope; + mobj_t *dest; + mobj_t *th; + + /* killough 1/18/98: this is why some missiles do not have smoke + * and some do. Also, internal demos start at random gametics, thus + * the bug in which revenants cause internal demos to go out of sync. + * + * killough 3/6/98: fix revenant internal demo bug by subtracting + * levelstarttic from gametic. + * + * killough 9/29/98: use new "basetic" so that demos stay in sync + * during pauses and menu activations, while retaining old demo sync. + * + * leveltime would have been better to use to start with in Doom, but + * since old demos were recorded using gametic, we must stick with it, + * and improvise around it (using leveltime causes desync across levels). + */ + + if ((gametic-basetic) & 3) + return; + + // spawn a puff of smoke behind the rocket + P_SpawnPuff(actor->x, actor->y, actor->z); + + th = P_SpawnMobj (actor->x-actor->momx, + actor->y-actor->momy, + actor->z, MT_SMOKE); + + th->momz = FRACUNIT; + th->tics -= P_Random(pr_tracer) & 3; + if (th->tics < 1) + th->tics = 1; + + // adjust direction + dest = actor->tracer; + + if (!dest || dest->health <= 0) + return; + + // change angle + exact = R_PointToAngle2(actor->x, actor->y, dest->x, dest->y); + + if (exact != actor->angle) { + if (exact - actor->angle > 0x80000000) + { + actor->angle -= TRACEANGLE; + if (exact - actor->angle < 0x80000000) + actor->angle = exact; + } + else + { + actor->angle += TRACEANGLE; + if (exact - actor->angle > 0x80000000) + actor->angle = exact; + } + } + + exact = actor->angle>>ANGLETOFINESHIFT; + actor->momx = FixedMul(actor->info->speed, finecosine[exact]); + actor->momy = FixedMul(actor->info->speed, finesine[exact]); + + // change slope + dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); + + dist = dist / actor->info->speed; + + if (dist < 1) + dist = 1; + + slope = (dest->z+40*FRACUNIT - actor->z) / dist; + + if (slope < actor->momz) + actor->momz -= FRACUNIT/8; + else + actor->momz += FRACUNIT/8; +} + +void A_SkelWhoosh(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + S_StartSound(actor,sfx_skeswg); +} + +void A_SkelFist(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + if (P_CheckMeleeRange(actor)) + { + int damage = ((P_Random(pr_skelfist)%10)+1)*6; + S_StartSound(actor, sfx_skepch); + P_DamageMobj(actor->target, actor, actor, damage); + } +} + +// +// PIT_VileCheck +// Detect a corpse that could be raised. +// + +mobj_t* corpsehit; +mobj_t* vileobj; +fixed_t viletryx; +fixed_t viletryy; + +static dboolean PIT_VileCheck(mobj_t *thing) +{ + int maxdist; + dboolean 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 (D_abs(thing->x-viletryx) > maxdist || D_abs(thing->y-viletryy) > maxdist) + return true; // not actually touching + +// Check to see if the radius and height are zero. If they are // phares +// then this is a crushed monster that has been turned into a // | +// gib. One of the options may be to ignore this guy. // V + +// Option 1: the original, buggy method, -> ghost (compatibility) +// Option 2: ressurect the monster, but not as a ghost +// Option 3: ignore the gib + +// if (Option3) // ^ +// if ((thing->height == 0) && (thing->radius == 0)) // | +// return true; // phares + + corpsehit = thing; + corpsehit->momx = corpsehit->momy = 0; + if (comp[comp_vile]) // phares + { // | + corpsehit->height <<= 2; // V + check = P_CheckPosition(corpsehit,corpsehit->x,corpsehit->y); + corpsehit->height >>= 2; + } + else + { + int height,radius; + + height = corpsehit->height; // save temporarily + radius = corpsehit->radius; // save temporarily + corpsehit->height = corpsehit->info->height; + corpsehit->radius = corpsehit->info->radius; + corpsehit->flags |= MF_SOLID; + check = P_CheckPosition(corpsehit,corpsehit->x,corpsehit->y); + corpsehit->height = height; // restore + corpsehit->radius = radius; // restore // ^ + corpsehit->flags &= ~MF_SOLID; + } // | + // phares + if (!check) + return true; // doesn't fit here + return false; // got one, so stop checking +} + +// +// A_VileChase +// Check for ressurecting a body +// + +void A_VileChase(mobj_t* actor) +{ + int xl, xh; + int yl, yh; + int bx, by; + + 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 = P_GetSafeBlockX(viletryx - bmaporgx - MAXRADIUS*2); + xh = P_GetSafeBlockX(viletryx - bmaporgx + MAXRADIUS*2); + yl = P_GetSafeBlockY(viletryy - bmaporgy - MAXRADIUS*2); + yh = P_GetSafeBlockY(viletryy - bmaporgy + MAXRADIUS*2); + + vileobj = actor; + for (bx=xl ; bx<=xh ; bx++) + { + for (by=yl ; by<=yh ; by++) + { + // Call PIT_VileCheck to check + // whether object is a corpse + // that canbe raised. + if (!P_BlockThingsIterator(bx,by,PIT_VileCheck)) + { + mobjinfo_t *info; + + // got one! + mobj_t* temp = actor->target; + actor->target = corpsehit; + A_FaceTarget(actor); + actor->target = temp; + + P_SetMobjState(actor, S_VILE_HEAL1); + S_StartSound(corpsehit, sfx_slop); + info = corpsehit->info; + + P_SetMobjState(corpsehit,info->raisestate); + + if (comp[comp_vile]) // phares + corpsehit->height <<= 2; // | + else // V + { + corpsehit->height = info->height; // fix Ghost bug + corpsehit->radius = info->radius; // fix Ghost bug + } // phares + + /* killough 7/18/98: + * friendliness is transferred from AV to raised corpse + */ + corpsehit->flags = + (info->flags & ~MF_FRIEND) | (actor->flags & MF_FRIEND); + corpsehit->flags = corpsehit->flags | MF_RESSURECTED;//e6y + + if (!((corpsehit->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) + totallive++; + + corpsehit->health = info->spawnhealth; + P_SetTarget(&corpsehit->target, NULL); // killough 11/98 + + if (mbf_features) + { /* kilough 9/9/98 */ + P_SetTarget(&corpsehit->lastenemy, NULL); + corpsehit->flags &= ~MF_JUSTHIT; + } + + /* killough 8/29/98: add to appropriate thread */ + P_UpdateThinker(&corpsehit->thinker); + + return; + } + } + } + } + A_Chase(actor); // Return to normal attack. +} + +// +// A_VileStart +// + +void A_VileStart(mobj_t *actor) +{ + S_StartSound(actor, sfx_vilatk); +} + +// +// A_Fire +// Keep fire in front of player unless out of sight +// + +void A_StartFire(mobj_t *actor) +{ + S_StartSound(actor,sfx_flamst); + A_Fire(actor); +} + +void A_FireCrackle(mobj_t* actor) +{ + S_StartSound(actor,sfx_flame); + A_Fire(actor); +} + +void A_Fire(mobj_t *actor) +{ + mobj_t* target; + unsigned an; + mobj_t *dest = actor->tracer; + + if (!dest) + return; + + target = P_SubstNullMobj(actor->target); + + // don't move it if the vile lost sight + if (!P_CheckSight(target, dest) ) + return; + + an = dest->angle >> ANGLETOFINESHIFT; + + P_UnsetThingPosition(actor); + actor->x = dest->x + FixedMul(24*FRACUNIT, finecosine[an]); + actor->y = dest->y + FixedMul(24*FRACUNIT, finesine[an]); + actor->z = dest->z; + P_SetThingPosition(actor); +} + +// +// A_VileTarget +// Spawn the hellfire +// + +void A_VileTarget(mobj_t *actor) +{ + mobj_t *fog; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + // killough 12/98: fix Vile fog coordinates // CPhipps - compatibility optioned + fog = P_SpawnMobj(actor->target->x, + (compatibility_level < lxdoom_1_compatibility) ? actor->target->x : actor->target->y, + actor->target->z,MT_FIRE); + + P_SetTarget(&actor->tracer, fog); + P_SetTarget(&fog->target, actor); + P_SetTarget(&fog->tracer, actor->target); + A_Fire(fog); +} + +// +// A_VileAttack +// + +void A_VileAttack(mobj_t *actor) +{ + mobj_t *fire; + int an; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + if (!P_CheckSight(actor, actor->target)) + return; + + S_StartSound(actor, sfx_barexp); + P_DamageMobj(actor->target, actor, actor, 20); + actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; + + an = actor->angle >> ANGLETOFINESHIFT; + + fire = actor->tracer; + + if (!fire) + return; + + // move the fire between the vile and the player + fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]); + fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]); + P_RadiusAttack(fire, actor, 70); +} + +// +// Mancubus attack, +// firing three missiles (bruisers) +// in three different directions? +// Doesn't look like it. +// + +#define FATSPREAD (ANG90/8) + +void A_FatRaise(mobj_t *actor) +{ + A_FaceTarget(actor); + S_StartSound(actor, sfx_manatk); +} + +void A_FatAttack1(mobj_t *actor) +{ + mobj_t *mo; + mobj_t* target; + int an; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + // Change direction to ... + actor->angle += FATSPREAD; + target = P_SubstNullMobj(actor->target); + P_SpawnMissile(actor, target, MT_FATSHOT); + + mo = P_SpawnMissile (actor, target, MT_FATSHOT); + mo->angle += FATSPREAD; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); +} + +void A_FatAttack2(mobj_t *actor) +{ + mobj_t *mo; + mobj_t* target; + int an; + + if (!actor->target) + return; + + A_FaceTarget(actor); + // Now here choose opposite deviation. + actor->angle -= FATSPREAD; + target = P_SubstNullMobj(actor->target); + P_SpawnMissile(actor, target, MT_FATSHOT); + + mo = P_SpawnMissile(actor, target, MT_FATSHOT); + mo->angle -= FATSPREAD*2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); +} + +void A_FatAttack3(mobj_t *actor) +{ + mobj_t *mo; + mobj_t* target; + int an; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + target = P_SubstNullMobj(actor->target); + + mo = P_SpawnMissile(actor, target, MT_FATSHOT); + mo->angle -= FATSPREAD/2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); + + mo = P_SpawnMissile(actor, target, MT_FATSHOT); + mo->angle += FATSPREAD/2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); +} + + +// +// SkullAttack +// Fly at the player like a missile. +// +#define SKULLSPEED (20*FRACUNIT) + +void A_SkullAttack(mobj_t *actor) +{ + mobj_t *dest; + angle_t an; + int dist; + + if (!actor->target) + return; + + dest = actor->target; + actor->flags |= MF_SKULLFLY; + + S_StartSound(actor, actor->info->attacksound); + A_FaceTarget(actor); + an = actor->angle >> ANGLETOFINESHIFT; + actor->momx = FixedMul(SKULLSPEED, finecosine[an]); + actor->momy = FixedMul(SKULLSPEED, finesine[an]); + dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); + dist = dist / SKULLSPEED; + + if (dist < 1) + dist = 1; + actor->momz = (dest->z+(dest->height>>1) - actor->z) / dist; +} + +void A_BetaSkullAttack(mobj_t *actor) +{ + int damage; + + if (compatibility_level < mbf_compatibility) + return; + + if (!actor->target || actor->target->type == MT_SKULL) + return; + + S_StartSound(actor, actor->info->attacksound); + A_FaceTarget(actor); + damage = (P_Random(pr_skullfly)%8+1)*actor->info->damage; + P_DamageMobj(actor->target, actor, actor, damage); +} + +void A_Stop(mobj_t *actor) +{ + if (compatibility_level < mbf_compatibility) + return; + + actor->momx = actor->momy = actor->momz = 0; +} + +// +// A_PainShootSkull +// Spawn a lost soul and launch it at the target +// + +static void A_PainShootSkull(mobj_t *actor, angle_t angle) +{ + fixed_t x,y,z; + mobj_t *newmobj; + angle_t an; + int prestep; + +// The original code checked for 20 skulls on the level, // phares +// and wouldn't spit another one if there were. If not in // phares +// compatibility mode, we remove the limit. // phares + // phares + if (comp[comp_pain]) /* killough 10/98: compatibility-optioned */ + { + // count total number of skulls currently on the level + int count = 0; + thinker_t *currentthinker = NULL; + while ((currentthinker = P_NextThinker(currentthinker,th_all)) != NULL) + if ((currentthinker->function == P_MobjThinker) + && ((mobj_t *)currentthinker)->type == MT_SKULL) + count++; + if (count > 20) // phares + return; // phares + } + + // okay, there's room for another one + + an = angle >> ANGLETOFINESHIFT; + + prestep = 4*FRACUNIT + 3*(actor->info->radius + mobjinfo[MT_SKULL].radius)/2; + + x = actor->x + FixedMul(prestep, finecosine[an]); + y = actor->y + FixedMul(prestep, finesine[an]); + z = actor->z + 8*FRACUNIT; + + if (comp[comp_skull]) /* killough 10/98: compatibility-optioned */ + newmobj = P_SpawnMobj(x, y, z, MT_SKULL); // phares + else // V + { + // Check whether the Lost Soul is being fired through a 1-sided + // wall or an impassible line, or a "monsters can't cross" line. + // If it is, then we don't allow the spawn. This is a bug fix, but + // it should be considered an enhancement, since it may disturb + // existing demos, so don't do it in compatibility mode. + + if (Check_Sides(actor,x,y)) + return; + + newmobj = P_SpawnMobj(x, y, z, MT_SKULL); + + // Check to see if the new Lost Soul's z value is above the + // ceiling of its new sector, or below the floor. If so, kill it. + + if ((newmobj->z > + (newmobj->subsector->sector->ceilingheight - newmobj->height)) || + (newmobj->z < newmobj->subsector->sector->floorheight)) + { + // kill it immediately + P_DamageMobj(newmobj,actor,actor,10000); + return; // ^ + } // | + } // phares + + /* killough 7/20/98: PEs shoot lost souls with the same friendliness */ + newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (actor->flags & MF_FRIEND); + + /* killough 8/29/98: add to appropriate thread */ + P_UpdateThinker(&newmobj->thinker); + + // Check for movements. + // killough 3/15/98: don't jump over dropoffs: + + if (!P_TryMove(newmobj, newmobj->x, newmobj->y, false)) + { + // kill it immediately + P_DamageMobj(newmobj, actor, actor, 10000); + return; + } + + P_SetTarget(&newmobj->target, actor->target); + A_SkullAttack(newmobj); +} + +// +// A_PainAttack +// Spawn a lost soul and launch it at the target +// + +void A_PainAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + A_PainShootSkull(actor, actor->angle); +} + +void A_PainDie(mobj_t *actor) +{ + A_Fall(actor); + A_PainShootSkull(actor, actor->angle+ANG90); + A_PainShootSkull(actor, actor->angle+ANG180); + A_PainShootSkull(actor, actor->angle+ANG270); +} + +void A_Scream(mobj_t *actor) +{ + int sound; + + switch (actor->info->deathsound) + { + case 0: + return; + + case sfx_podth1: + case sfx_podth2: + case sfx_podth3: + sound = sfx_podth1 + P_Random(pr_scream)%3; + break; + + case sfx_bgdth1: + case sfx_bgdth2: + sound = sfx_bgdth1 + P_Random(pr_scream)%2; + break; + + default: + sound = actor->info->deathsound; + break; + } + + // Check for bosses. + if (actor->type==MT_SPIDER || actor->type == MT_CYBORG) + S_StartSound(NULL, sound); // full volume + else + S_StartSound(actor, sound); +} + +void A_XScream(mobj_t *actor) +{ + S_StartSound(actor, sfx_slop); +} + +void A_SkullPop(mobj_t *actor) +{ + mobj_t *mo; + player_t *player; + int sfx_id; + + if (demorecording || demoplayback) + return; + + sfx_id = (I_GetSfxLumpNum(&S_sfx[sfx_gibdth]) < 0 ? sfx_pldeth : sfx_gibdth); + S_StartSound(actor, sfx_id); + + actor->flags &= ~MF_SOLID; + mo = P_SpawnMobj(actor->x, actor->y, actor->z + 48 * FRACUNIT, MT_GIBDTH); + //mo->target = actor; + mo->momx = (P_Random(pr_misc) - P_Random(pr_misc)) << 9; + mo->momy = (P_Random(pr_misc) - P_Random(pr_misc)) << 9; + mo->momz = FRACUNIT * 2 + (P_Random(pr_misc) << 6); + // Attach player mobj to bloody skull + player = actor->player; + actor->player = NULL; + mo->player = player; + mo->health = actor->health; + mo->angle = actor->angle; + mo->pitch = 0; + if (player) + { + player->mo = mo; + player->damagecount = 32; + } + +#if 0 + if (player) + { + int i; + int plr = player - players; + int delay = 4 * TICRATE; + static const char *msg = "e6ylity"; + + SetCustomMessage(plr, msg, delay, 3*TICRATE, CR_RED, sfx_secret); + for (i = 0; i < 6; i++) + { + delay += 1; SetCustomMessage(plr, msg, delay, 3*TICRATE, CR_GOLD, sfx_None); + delay += 1; SetCustomMessage(plr, msg, delay, 3*TICRATE, CR_RED, sfx_None); + } + } +#endif +} + +void A_Pain(mobj_t *actor) +{ + if (actor->info->painsound) + S_StartSound(actor, actor->info->painsound); +} + +void A_Fall(mobj_t *actor) +{ + // actor is on ground, it can be walked over + actor->flags &= ~MF_SOLID; +} + +// +// A_Explode +// +void A_Explode(mobj_t *thingy) +{ + P_RadiusAttack( thingy, thingy->target, 128 ); +} + +// +// A_BossDeath +// Possibly trigger special effects +// if on first boss level +// + +void A_BossDeath(mobj_t *mo) +{ + thinker_t *th; + line_t junk; + int i; + + // numbossactions == 0 means to use the defaults. + // numbossactions == -1 means to do nothing. + // positive values mean to check the list of boss actions and run all that apply. + if (gamemapinfo && gamemapinfo->numbossactions != 0) + { + if (gamemapinfo->numbossactions < 0) return; + + // make sure there is a player alive for victory + for (i=0; i 0) + break; + + if (i==MAXPLAYERS) + return; // no one left alive, so do not end game + + for (i = 0; i < gamemapinfo->numbossactions; i++) + { + if (gamemapinfo->bossactions[i].type == mo->type) + break; + } + if (i >= gamemapinfo->numbossactions) + return; // no matches found + + // scan the remaining thinkers to see + // if all bosses are dead + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + if (th->function == P_MobjThinker) + { + mobj_t *mo2 = (mobj_t *) th; + if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) + return; // other boss not dead + } + for (i = 0; i < gamemapinfo->numbossactions; i++) + { + if (gamemapinfo->bossactions[i].type == mo->type) + { + junk = *lines; + junk.special = (short)gamemapinfo->bossactions[i].special; + junk.tag = (short)gamemapinfo->bossactions[i].tag; + // use special semantics for line activation to block problem types. + if (!P_UseSpecialLine(mo, &junk, 0, true)) + P_CrossSpecialLine(&junk, 0, mo, true); + } + } + + return; + } + + if (gamemode == commercial) + { + if (gamemap != 7) + return; + + if ((mo->type != MT_FATSO) + && (mo->type != MT_BABY)) + return; + } + else + { + // e6y + // Additional check of gameepisode is necessary, because + // there is no right or wrong solution for E4M6 in original EXEs, + // there's nothing to emulate. + if (comp[comp_666] && gameepisode < 4) + { + // e6y + // Only following checks are present in doom2.exe ver. 1.666 and 1.9 + // instead of separate checks for each episode in doomult.exe, plutonia.exe and tnt.exe + // There is no more desync on doom.wad\episode3.lmp + // http://www.doomworld.com/idgames/index.php?id=6909 + if (gamemap != 8) + return; + if (mo->type == MT_BRUISER && gameepisode != 1) + return; + } + else + { + switch(gameepisode) + { + case 1: + if (gamemap != 8) + return; + + if (mo->type != MT_BRUISER) + return; + break; + + case 2: + if (gamemap != 8) + return; + + if (mo->type != MT_CYBORG) + return; + break; + + case 3: + if (gamemap != 8) + return; + + if (mo->type != MT_SPIDER) + return; + + break; + + case 4: + switch(gamemap) + { + case 6: + if (mo->type != MT_CYBORG) + return; + break; + + case 8: + if (mo->type != MT_SPIDER) + return; + break; + + default: + return; + break; + } + break; + + default: + if (gamemap != 8) + return; + break; + } + } + + } + + // make sure there is a player alive for victory + for (i=0; i 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 == P_MobjThinker) + { + mobj_t *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_DoFloor(&junk,lowerFloorToLowest); + return; + } + + if (mo->type == MT_BABY) + { + junk.tag = 667; + EV_DoFloor(&junk,raiseToTexture); + return; + } + } + } + else + { + switch(gameepisode) + { + case 1: + junk.tag = 666; + EV_DoFloor(&junk, lowerFloorToLowest); + return; + break; + + case 4: + switch(gamemap) + { + case 6: + junk.tag = 666; + EV_DoDoor(&junk, blazeOpen); + return; + break; + + case 8: + junk.tag = 666; + EV_DoFloor(&junk, lowerFloorToLowest); + return; + break; + } + } + } + G_ExitLevel(); +} + + +void A_Hoof (mobj_t* mo) +{ + S_StartSound(mo, sfx_hoof); + A_Chase(mo); +} + +void A_Metal(mobj_t *mo) +{ + S_StartSound(mo, sfx_metal); + A_Chase(mo); +} + +void A_BabyMetal(mobj_t *mo) +{ + S_StartSound(mo, sfx_bspwlk); + A_Chase(mo); +} + +void A_OpenShotgun2(player_t *player, pspdef_t *psp) +{ + S_StartSound(player->mo, sfx_dbopn); +} + +void A_LoadShotgun2(player_t *player, pspdef_t *psp) +{ + S_StartSound(player->mo, sfx_dbload); +} + +void A_CloseShotgun2(player_t *player, pspdef_t *psp) +{ + S_StartSound(player->mo, sfx_dbcls); + A_ReFire(player,psp); +} + +// killough 2/7/98: Remove limit on icon landings: +mobj_t **braintargets; +int numbraintargets_alloc; +int numbraintargets; + +struct brain_s brain; // killough 3/26/98: global state of boss brain + +// killough 3/26/98: initialize icon landings at level startup, +// rather than at boss wakeup, to prevent savegame-related crashes + +void P_SpawnBrainTargets(void) // killough 3/26/98: renamed old function +{ + thinker_t *thinker; + + // find all the target spots + numbraintargets = 0; + brain.targeton = 0; + brain.easy = 0; // killough 3/26/98: always init easy to 0 + + for (thinker = thinkercap.next ; + thinker != &thinkercap ; + thinker = thinker->next) + if (thinker->function == P_MobjThinker) + { + mobj_t *m = (mobj_t *) thinker; + + if (m->type == MT_BOSSTARGET ) + { // killough 2/7/98: remove limit on icon landings: + if (numbraintargets >= numbraintargets_alloc) + braintargets = realloc(braintargets, + (numbraintargets_alloc = numbraintargets_alloc ? + numbraintargets_alloc*2 : 32) *sizeof *braintargets); + braintargets[numbraintargets++] = m; + } + } +} + +void A_BrainAwake(mobj_t *mo) +{ + //e6y + if (demo_compatibility && !prboom_comp[PC_BOOM_BRAINAWAKE].state) + { + brain.targeton = 0; + brain.easy = 0; + } + + S_StartSound(NULL,sfx_bossit); // killough 3/26/98: only generates sound now +} + +void A_BrainPain(mobj_t *mo) +{ + S_StartSound(NULL,sfx_bospn); +} + +void A_BrainScream(mobj_t *mo) +{ + int x; + for (x=mo->x - 196*FRACUNIT ; x< mo->x + 320*FRACUNIT ; x+= FRACUNIT*8) + { + int y = mo->y - 320*FRACUNIT; + int z = 128 + P_Random(pr_brainscream)*2*FRACUNIT; + mobj_t *th = P_SpawnMobj (x,y,z, MT_ROCKET); + th->momz = P_Random(pr_brainscream)*512; + P_SetMobjState(th, S_BRAINEXPLODE1); + th->tics -= P_Random(pr_brainscream)&7; + if (th->tics < 1) + th->tics = 1; + } + S_StartSound(NULL,sfx_bosdth); +} + +void A_BrainExplode(mobj_t *mo) +{ // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_brainexp); + int x = mo->x + (t - P_Random(pr_brainexp))*2048; + int y = mo->y; + int z = 128 + P_Random(pr_brainexp)*2*FRACUNIT; + mobj_t *th = P_SpawnMobj(x,y,z, MT_ROCKET); + th->momz = P_Random(pr_brainexp)*512; + P_SetMobjState(th, S_BRAINEXPLODE1); + th->tics -= P_Random(pr_brainexp)&7; + if (th->tics < 1) + th->tics = 1; +} + +void A_BrainDie(mobj_t *mo) +{ + G_ExitLevel(); +} + +void A_BrainSpit(mobj_t *mo) +{ + mobj_t *targ, *newmobj; + + if (!numbraintargets) // killough 4/1/98: ignore if no targets + return; + + brain.easy ^= 1; // killough 3/26/98: use brain struct + if (gameskill <= sk_easy && !brain.easy) + return; + + // shoot a cube at current target + targ = braintargets[brain.targeton++]; // killough 3/26/98: + brain.targeton %= numbraintargets; // Use brain struct for targets + + // spawn brain missile + newmobj = P_SpawnMissile(mo, targ, MT_SPAWNSHOT); + + // e6y: do not crash with 'incorrect' DEHs + if (!newmobj || !newmobj->state || newmobj->momy == 0 || newmobj->state->tics == 0) + I_Error("A_BrainSpit: can't spawn brain missile (incorrect DEH)"); + + P_SetTarget(&newmobj->target, targ); + newmobj->reactiontime = (short)(((targ->y-mo->y)/newmobj->momy)/newmobj->state->tics); + + // killough 7/18/98: brain friendliness is transferred + newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND); + + // killough 8/29/98: add to appropriate thread + P_UpdateThinker(&newmobj->thinker); + + S_StartSound(NULL, sfx_bospit); +} + +// travelling cube sound +void A_SpawnSound(mobj_t *mo) +{ + S_StartSound(mo,sfx_boscub); + A_SpawnFly(mo); +} + +void A_SpawnFly(mobj_t *mo) +{ + mobj_t *newmobj; + mobj_t *fog; + mobj_t *targ; + int r; + mobjtype_t type; + + if (--mo->reactiontime) + return; // still flying + + targ = P_SubstNullMobj(mo->target); + + // First spawn teleport fog. + fog = P_SpawnMobj(targ->x, targ->y, targ->z, MT_SPAWNFIRE); + S_StartSound(fog, sfx_telept); + + // Randomly select monster to spawn. + r = P_Random(pr_spawnfly); + + // Probability distribution (kind of :), decreasing likelihood. + if ( r<50 ) + type = MT_TROOP; + else if (r<90) + type = MT_SERGEANT; + else if (r<120) + type = MT_SHADOWS; + else if (r<130) + type = MT_PAIN; + else if (r<160) + type = MT_HEAD; + else if (r<162) + type = MT_VILE; + else if (r<172) + type = MT_UNDEAD; + else if (r<192) + type = MT_BABY; + else if (r<222) + type = MT_FATSO; + else if (r<246) + type = MT_KNIGHT; + else + type = MT_BRUISER; + + newmobj = P_SpawnMobj(targ->x, targ->y, targ->z, type); + + /* killough 7/18/98: brain friendliness is transferred */ + newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND); + + //e6y: monsters spawned by Icon of Sin should not be countable for total killed. + newmobj->flags |= MF_RESSURECTED; + + /* killough 8/29/98: add to appropriate thread */ + P_UpdateThinker(&newmobj->thinker); + + if (P_LookForTargets(newmobj,true)) /* killough 9/4/98 */ + P_SetMobjState(newmobj, newmobj->info->seestate); + + // telefrag anything in this spot + P_TeleportMove(newmobj, newmobj->x, newmobj->y, true); /* killough 8/9/98 */ + + // remove self (i.e., cube). + P_RemoveMobj(mo); +} + +void A_PlayerScream(mobj_t *mo) +{ + int sound = sfx_pldeth; // Default death sound. + if (gamemode != shareware && mo->health < -50) + sound = sfx_pdiehi; // IF THE PLAYER DIES LESS THAN -50% WITHOUT GIBBING + S_StartSound(mo, sound); +} + +/* cph - MBF-added codepointer functions */ + +// killough 11/98: kill an object +void A_Die(mobj_t *actor) +{ + if (compatibility_level < lxdoom_1_compatibility && + !prboom_comp[PC_APPLY_MBF_CODEPOINTERS_TO_ANY_COMPLEVEL].state) + return; + + P_DamageMobj(actor, NULL, NULL, actor->health); +} + +// +// A_Detonate +// killough 8/9/98: same as A_Explode, except that the damage is variable +// + +void A_Detonate(mobj_t *mo) +{ + if (compatibility_level < lxdoom_1_compatibility && + !prboom_comp[PC_APPLY_MBF_CODEPOINTERS_TO_ANY_COMPLEVEL].state) + return; + + P_RadiusAttack(mo, mo->target, mo->info->damage); +} + +// +// killough 9/98: a mushroom explosion effect, sorta :) +// Original idea: Linguica +// + +void A_Mushroom(mobj_t *actor) +{ + int i, j, n = actor->info->damage; + + // Mushroom parameters are part of code pointer's state + dboolean mbf = (compatibility_level == mbf_compatibility && + !prboom_comp[PC_DO_NOT_USE_MISC12_FRAME_PARAMETERS_IN_A_MUSHROOM].state); + fixed_t misc1 = ((mbf && actor->state->misc1) ? actor->state->misc1 : FRACUNIT*4); + fixed_t misc2 = ((mbf && actor->state->misc2) ? actor->state->misc2 : FRACUNIT/2); + + if (compatibility_level < lxdoom_1_compatibility && + !prboom_comp[PC_APPLY_MBF_CODEPOINTERS_TO_ANY_COMPLEVEL].state) + return; + + A_Explode(actor); // First make normal explosion + + // Now launch mushroom cloud + for (i = -n; i <= n; i += 8) + for (j = -n; j <= n; j += 8) + { + mobj_t target = *actor, *mo; + target.x += i << FRACBITS; // Aim in many directions from source + target.y += j << FRACBITS; + target.z += P_AproxDistance(i,j) * misc1; // Aim up fairly high + mo = P_SpawnMissile(actor, &target, MT_FATSHOT); // Launch fireball + mo->momx = FixedMul(mo->momx, misc2); + mo->momy = FixedMul(mo->momy, misc2); // Slow down a bit + mo->momz = FixedMul(mo->momz, misc2); + mo->flags &= ~MF_NOGRAVITY; // Make debris fall under gravity + } +} + +// +// killough 11/98 +// +// The following were inspired by Len Pitre +// +// A small set of highly-sought-after code pointers +// + +void A_Spawn(mobj_t *mo) +{ + if (compatibility_level < lxdoom_1_compatibility && + !prboom_comp[PC_APPLY_MBF_CODEPOINTERS_TO_ANY_COMPLEVEL].state) + return; + + if (mo->state->misc1) + { + mobj_t *newmobj = + P_SpawnMobj(mo->x, mo->y, (mo->state->misc2 << FRACBITS) + mo->z, + mo->state->misc1 - 1); + if (compatibility_level == mbf_compatibility && + !prboom_comp[PC_DO_NOT_INHERIT_FRIENDLYNESS_FLAG_ON_SPAWN].state) + /* CPhipps - no friendlyness (yet)*/ //e6y: why not? + newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND); + } +} + +void A_Turn(mobj_t *mo) +{ + if (compatibility_level < lxdoom_1_compatibility && + !prboom_comp[PC_APPLY_MBF_CODEPOINTERS_TO_ANY_COMPLEVEL].state) + return; + + mo->angle += (unsigned int)(((uint_64_t) mo->state->misc1 << 32) / 360); +} + +void A_Face(mobj_t *mo) +{ + if (compatibility_level < lxdoom_1_compatibility && + !prboom_comp[PC_APPLY_MBF_CODEPOINTERS_TO_ANY_COMPLEVEL].state) + return; + + mo->angle = (unsigned int)(((uint_64_t) mo->state->misc1 << 32) / 360); +} + +void A_Scratch(mobj_t *mo) +{ + if (compatibility_level < lxdoom_1_compatibility && + !prboom_comp[PC_APPLY_MBF_CODEPOINTERS_TO_ANY_COMPLEVEL].state) + return; + + mo->target && (A_FaceTarget(mo), P_CheckMeleeRange(mo)) ? + mo->state->misc2 ? S_StartSound(mo, mo->state->misc2) : (void) 0, + P_DamageMobj(mo->target, mo, mo, mo->state->misc1) : (void) 0; +} + +void A_PlaySound(mobj_t *mo) +{ + if (compatibility_level < lxdoom_1_compatibility && + !prboom_comp[PC_APPLY_MBF_CODEPOINTERS_TO_ANY_COMPLEVEL].state) + return; + + S_StartSound(mo->state->misc2 ? NULL : mo, mo->state->misc1); +} + +void A_RandomJump(mobj_t *mo) +{ + if (compatibility_level < lxdoom_1_compatibility && + !prboom_comp[PC_APPLY_MBF_CODEPOINTERS_TO_ANY_COMPLEVEL].state) + return; + + if (P_Random(pr_randomjump) < mo->state->misc2) + P_SetMobjState(mo, mo->state->misc1); +} + +// +// This allows linedef effects to be activated inside deh frames. +// + +void A_LineEffect(mobj_t *mo) +{ + static line_t junk; + player_t player; + player_t *oldplayer; + + if (compatibility_level < lxdoom_1_compatibility && + !prboom_comp[PC_APPLY_MBF_CODEPOINTERS_TO_ANY_COMPLEVEL].state) + return; + + junk = *lines; + oldplayer = mo->player; + mo->player = &player; + player.health = 100; + junk.special = (short)mo->state->misc1; + if (!junk.special) + return; + junk.tag = (short)mo->state->misc2; + if (!P_UseSpecialLine(mo, &junk, 0, false)) + P_CrossSpecialLine(&junk, 0, mo, false); + mo->state->misc1 = junk.special; + mo->player = oldplayer; +} diff --git a/src/p_enemy.h b/src/p_enemy.h new file mode 100644 index 0000000..5e60b91 --- /dev/null +++ b/src/p_enemy.h @@ -0,0 +1,123 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Enemy thinking, AI. + * Action Pointer Functions + * that are associated with states/frames. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_ENEMY__ +#define __P_ENEMY__ + +#include "p_mobj.h" + +void P_NoiseAlert (mobj_t *target, mobj_t *emmiter); +void P_SpawnBrainTargets(void); /* killough 3/26/98: spawn icon landings */ + +extern struct brain_s { /* killough 3/26/98: global state of boss brain */ + int easy, targeton; +} brain; + +// ******************************************************************** +// Function addresses or Code Pointers +// ******************************************************************** +// These function addresses are the Code Pointers that have been +// modified for years by Dehacked enthusiasts. The new BEX format +// allows more extensive changes (see d_deh.c) + +// Doesn't work with g++, needs actionf_p1 +void A_Explode(mobj_t *); +void A_Pain(mobj_t *); +void A_PlayerScream(mobj_t *); +void A_Fall(mobj_t *); +void A_XScream(mobj_t *); +void A_Look(mobj_t *); +void A_Chase(mobj_t *); +void A_FaceTarget(mobj_t *); +void A_PosAttack(mobj_t *); +void A_Scream(mobj_t *); +void A_SPosAttack(mobj_t *); +void A_VileChase(mobj_t *); +void A_VileStart(mobj_t *); +void A_VileTarget(mobj_t *); +void A_VileAttack(mobj_t *); +void A_StartFire(mobj_t *); +void A_Fire(mobj_t *); +void A_FireCrackle(mobj_t *); +void A_Tracer(mobj_t *); +void A_SkelWhoosh(mobj_t *); +void A_SkelFist(mobj_t *); +void A_SkelMissile(mobj_t *); +void A_FatRaise(mobj_t *); +void A_FatAttack1(mobj_t *); +void A_FatAttack2(mobj_t *); +void A_FatAttack3(mobj_t *); +void A_BossDeath(mobj_t *); +void A_CPosAttack(mobj_t *); +void A_CPosRefire(mobj_t *); +void A_TroopAttack(mobj_t *); +void A_SargAttack(mobj_t *); +void A_HeadAttack(mobj_t *); +void A_BruisAttack(mobj_t *); +void A_SkullAttack(mobj_t *); +void A_Metal(mobj_t *); +void A_SpidRefire(mobj_t *); +void A_BabyMetal(mobj_t *); +void A_BspiAttack(mobj_t *); +void A_Hoof(mobj_t *); +void A_CyberAttack(mobj_t *); +void A_PainAttack(mobj_t *); +void A_PainDie(mobj_t *); +void A_KeenDie(mobj_t *); +void A_BrainPain(mobj_t *); +void A_BrainScream(mobj_t *); +void A_BrainDie(mobj_t *); +void A_BrainAwake(mobj_t *); +void A_BrainSpit(mobj_t *); +void A_SpawnSound(mobj_t *); +void A_SpawnFly(mobj_t *); +void A_BrainExplode(mobj_t *); +void A_Die(mobj_t *); +void A_Detonate(mobj_t *); /* killough 8/9/98: detonate a bomb or other device */ +void A_Mushroom(mobj_t *); /* killough 10/98: mushroom effect */ +void A_Spawn(mobj_t *); // killough 11/98 +void A_Turn(mobj_t *); // killough 11/98 +void A_Face(mobj_t *); // killough 11/98 +void A_Scratch(mobj_t *); // killough 11/98 +void A_PlaySound(mobj_t *); // killough 11/98 +void A_RandomJump(mobj_t *); // killough 11/98 +void A_LineEffect(mobj_t *); // killough 11/98 + +void A_BetaSkullAttack(mobj_t *); // killough 10/98: beta lost souls attacked different +void A_Stop(mobj_t *); + +void A_SkullPop(mobj_t *); + +#endif // __P_ENEMY__ diff --git a/src/p_floor.c b/src/p_floor.c new file mode 100644 index 0000000..4f61eb9 --- /dev/null +++ b/src/p_floor.c @@ -0,0 +1,1161 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * General plane mover and floor mover action routines + * Floor motion, pure changer types, raising stairs. donuts, elevators + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "r_main.h" +#include "p_map.h" +#include "p_spec.h" +#include "p_tick.h" +#include "s_sound.h" +#include "sounds.h" +#include "lprintf.h" +#include "g_overflow.h" +#include "e6y.h"//e6y + +//e6y +#define STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE 10 + +/////////////////////////////////////////////////////////////////////// +// +// Plane (floor or ceiling), Floor motion and Elevator action routines +// +/////////////////////////////////////////////////////////////////////// + +// +// T_MovePlane() +// +// Move a plane (floor or ceiling) and check for crushing. Called +// every tick by all actions that move floors or ceilings. +// +// Passed the sector to move a plane in, the speed to move it at, +// the dest height it is to achieve, whether it crushes obstacles, +// whether it moves a floor or ceiling, and the direction up or down +// to move. +// +// Returns a result_e: +// ok - plane moved normally, has not achieved destination yet +// pastdest - plane moved normally and is now at destination height +// crushed - plane encountered an obstacle, is holding until removed +// +result_e T_MovePlane +( sector_t* sector, + fixed_t speed, + fixed_t dest, + dboolean crush, + int floorOrCeiling, + int direction ) +{ + dboolean flag; + fixed_t lastpos; + fixed_t destheight; //jff 02/04/98 used to keep floors/ceilings + // from moving thru each other + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + gld_UpdateSplitData(sector); + } +#endif + + switch(floorOrCeiling) + { + case 0: + // Moving a floor + switch(direction) + { + case -1: + // Moving a floor down + if (sector->floorheight - speed < dest) + { + lastpos = sector->floorheight; + sector->floorheight = dest; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + if (flag == true) + { + sector->floorheight =lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + } + return pastdest; + } + else + { + lastpos = sector->floorheight; + sector->floorheight -= speed; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + /* cph - make more compatible with original Doom, by + * reintroducing this code. This means floors can't lower + * if objects are stuck in the ceiling */ + if ((flag == true) && comp[comp_floors]) { + sector->floorheight = lastpos; + P_ChangeSector(sector,crush); + return crushed; + } + } + break; + + case 1: + // Moving a floor up + // jff 02/04/98 keep floor from moving thru ceilings + // jff 2/22/98 weaken check to demo_compatibility + destheight = (comp[comp_floors] || destceilingheight)? + dest : sector->ceilingheight; + if (sector->floorheight + speed > destheight) + { + lastpos = sector->floorheight; + sector->floorheight = destheight; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + if (flag == true) + { + sector->floorheight = lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + } + return pastdest; + } + else + { + // crushing is possible + lastpos = sector->floorheight; + sector->floorheight += speed; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + if (flag == true) + { + /* jff 1/25/98 fix floor crusher */ + if (comp[comp_floors]) { + + //e6y: warning about potential desynch + if (crush == STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE) + { + lprintf(LO_WARN, "T_MovePlane: Stairs which can potentially crush may lead to desynch in compatibility mode.\n"); + lprintf(LO_WARN, " gametic: %d, sector: %d, complevel: %d\n", gametic, sector->iSectorID, compatibility_level); + } + + if (crush == true) + return crushed; + } + sector->floorheight = lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + return crushed; + } + } + break; + } + break; + + case 1: + // moving a ceiling + switch(direction) + { + case -1: + // moving a ceiling down + // jff 02/04/98 keep ceiling from moving thru floors + // jff 2/22/98 weaken check to demo_compatibility + destheight = (comp[comp_floors] || dest>sector->floorheight)? + dest : sector->floorheight; + if (sector->ceilingheight - speed < destheight) + { + lastpos = sector->ceilingheight; + sector->ceilingheight = destheight; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + + if (flag == true) + { + sector->ceilingheight = lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + } + return pastdest; + } + else + { + // crushing is possible + lastpos = sector->ceilingheight; + sector->ceilingheight -= speed; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + + if (flag == true) + { + if (crush == true) + return crushed; + sector->ceilingheight = lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + return crushed; + } + } + break; + + case 1: + // moving a ceiling up + if (sector->ceilingheight + speed > dest) + { + lastpos = sector->ceilingheight; + sector->ceilingheight = dest; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + if (flag == true) + { + sector->ceilingheight = lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + } + return pastdest; + } + else + { + lastpos = sector->ceilingheight; + sector->ceilingheight += speed; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + } + break; + } + break; + } + return ok; +} + +// +// T_MoveFloor() +// +// Move a floor to it's destination (up or down). +// Called once per tick for each moving floor. +// +// Passed a floormove_t structure that contains all pertinent info about the +// move. See P_SPEC.H for fields. +// No return. +// +// jff 02/08/98 all cases with labels beginning with gen added to support +// generalized line type behaviors. + +void T_MoveFloor(floormove_t* floor) +{ + result_e res; + + res = T_MovePlane // move the floor + ( + floor->sector, + floor->speed, + floor->floordestheight, + floor->crush, + 0, + floor->direction + ); + + if (!(leveltime&7)) // make the floormove sound + S_StartSound((mobj_t *)&floor->sector->soundorg, sfx_stnmov); + + if (res == pastdest) // if destination height is reached + { + if (floor->direction == 1) // going up + { + switch(floor->type) // handle texture/type changes + { + case donutRaise: + floor->sector->special = floor->newspecial; + floor->sector->floorpic = floor->texture; + break; + case genFloorChgT: + case genFloorChg0: + floor->sector->special = floor->newspecial; + //jff add to fix bug in special transfers from changes + floor->sector->oldspecial = floor->oldspecial; + //fall thru + case genFloorChg: + floor->sector->floorpic = floor->texture; + break; + default: + break; + } + } + else if (floor->direction == -1) // going down + { + switch(floor->type) // handle texture/type changes + { + case lowerAndChange: + floor->sector->special = floor->newspecial; + //jff add to fix bug in special transfers from changes + floor->sector->oldspecial = floor->oldspecial; + floor->sector->floorpic = floor->texture; + break; + case genFloorChgT: + case genFloorChg0: + floor->sector->special = floor->newspecial; + //jff add to fix bug in special transfers from changes + floor->sector->oldspecial = floor->oldspecial; + //fall thru + case genFloorChg: + floor->sector->floorpic = floor->texture; + break; + default: + break; + } + } + + floor->sector->floordata = NULL; //jff 2/22/98 + P_RemoveThinker(&floor->thinker);//remove this floor from list of movers + + //jff 2/26/98 implement stair retrigger lockout while still building + // note this only applies to the retriggerable generalized stairs + + if (floor->sector->stairlock==-2) // if this sector is stairlocked + { + sector_t *sec = floor->sector; + sec->stairlock=-1; // thinker done, promote lock to -1 + + while (sec->prevsec!=-1 && sectors[sec->prevsec].stairlock!=-2) + sec = §ors[sec->prevsec]; // search for a non-done thinker + if (sec->prevsec==-1) // if all thinkers previous are done + { + sec = floor->sector; // search forward + while (sec->nextsec!=-1 && sectors[sec->nextsec].stairlock!=-2) + sec = §ors[sec->nextsec]; + if (sec->nextsec==-1) // if all thinkers ahead are done too + { + while (sec->prevsec!=-1) // clear all locks + { + sec->stairlock = 0; + sec = §ors[sec->prevsec]; + } + sec->stairlock = 0; + } + } + } + + // Moving floors (but not plats) in versions <= v1.2 did not + // make floor stop sound + if (compatibility_level > doom_12_compatibility) + S_StartSound((mobj_t *)&floor->sector->soundorg, sfx_pstop); + } +} + +// +// T_MoveElevator() +// +// Move an elevator to it's destination (up or down) +// Called once per tick for each moving floor. +// +// Passed an elevator_t structure that contains all pertinent info about the +// move. See P_SPEC.H for fields. +// No return. +// +// jff 02/22/98 added to support parallel floor/ceiling motion +// +void T_MoveElevator(elevator_t* elevator) +{ + result_e res; + + if (elevator->direction<0) // moving down + { + res = T_MovePlane //jff 4/7/98 reverse order of ceiling/floor + ( + elevator->sector, + elevator->speed, + elevator->ceilingdestheight, + 0, + 1, // move floor + elevator->direction + ); + if (res==ok || res==pastdest) // jff 4/7/98 don't move ceil if blocked + T_MovePlane + ( + elevator->sector, + elevator->speed, + elevator->floordestheight, + 0, + 0, // move ceiling + elevator->direction + ); + } + else // up + { + res = T_MovePlane //jff 4/7/98 reverse order of ceiling/floor + ( + elevator->sector, + elevator->speed, + elevator->floordestheight, + 0, + 0, // move ceiling + elevator->direction + ); + if (res==ok || res==pastdest) // jff 4/7/98 don't move floor if blocked + T_MovePlane + ( + elevator->sector, + elevator->speed, + elevator->ceilingdestheight, + 0, + 1, // move floor + elevator->direction + ); + } + + // make floor move sound + if (!(leveltime&7)) + S_StartSound((mobj_t *)&elevator->sector->soundorg, sfx_stnmov); + + if (res == pastdest) // if destination height acheived + { + elevator->sector->floordata = NULL; //jff 2/22/98 + elevator->sector->ceilingdata = NULL; //jff 2/22/98 + P_RemoveThinker(&elevator->thinker); // remove elevator from actives + + // make floor stop sound + S_StartSound((mobj_t *)&elevator->sector->soundorg, sfx_pstop); + } +} + +/////////////////////////////////////////////////////////////////////// +// +// Floor motion linedef handlers +// +/////////////////////////////////////////////////////////////////////// + +// +// EV_DoFloor() +// +// Handle regular and extended floor types +// +// Passed the line that activated the floor and the type of floor motion +// Returns true if a thinker was created. +// +int EV_DoFloor +( line_t* line, + floor_e floortype ) +{ + int secnum; + int rtn; + int i; + sector_t* sec; + floormove_t* floor; + + secnum = -1; + rtn = 0; + + if (ProcessNoTagLines(line, &sec, &secnum)) {if (zerotag_manual) goto manual_floor; else {return rtn;}};//e6y + // move all floors with the same tag as the linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_floor://e6y + // Don't start a second thinker on the same floor + if (P_SectorActive(floor_special,sec)) //jff 2/23/98 + { if (!zerotag_manual) continue; else return rtn; }//e6y + + // new floor thinker + rtn = 1; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + sec->floordata = floor; //jff 2/22/98 + floor->thinker.function = T_MoveFloor; + floor->type = floortype; + floor->crush = false; + + // setup the thinker according to the linedef type + switch(floortype) + { + case lowerFloor: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = P_FindHighestFloorSurrounding(sec); + break; + + //jff 02/03/30 support lowering floor by 24 absolute + case lowerFloor24: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + 24 * FRACUNIT; + break; + + //jff 02/03/30 support lowering floor by 32 absolute (fast) + case lowerFloor32Turbo: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED*4; + floor->floordestheight = floor->sector->floorheight + 32 * FRACUNIT; + break; + + case lowerFloorToLowest: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = P_FindLowestFloorSurrounding(sec); + break; + + //jff 02/03/30 support lowering floor to next lowest floor + case lowerFloorToNearest: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + P_FindNextLowestFloor(sec,floor->sector->floorheight); + break; + + case turboLower: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED * 4; + floor->floordestheight = P_FindHighestFloorSurrounding(sec); + if (compatibility_level == doom_12_compatibility || + floor->floordestheight != sec->floorheight) + floor->floordestheight += 8*FRACUNIT; + break; + + case raiseFloorCrush: + floor->crush = true; + // fallthrough + case raiseFloor: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = P_FindLowestCeilingSurrounding(sec); + if (floor->floordestheight > sec->ceilingheight) + floor->floordestheight = sec->ceilingheight; + floor->floordestheight -= (8*FRACUNIT)*(floortype == raiseFloorCrush); + break; + + case raiseFloorTurbo: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED*4; + floor->floordestheight = P_FindNextHighestFloor(sec,sec->floorheight); + break; + + case raiseFloorToNearest: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = P_FindNextHighestFloor(sec,sec->floorheight); + break; + + case raiseFloor24: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + 24 * FRACUNIT; + break; + + // jff 2/03/30 support straight raise by 32 (fast) + case raiseFloor32Turbo: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED*4; + floor->floordestheight = floor->sector->floorheight + 32 * FRACUNIT; + break; + + case raiseFloor512: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + 512 * FRACUNIT; + break; + + case raiseFloor24AndChange: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + 24 * FRACUNIT; + sec->floorpic = line->frontsector->floorpic; + sec->special = line->frontsector->special; + //jff 3/14/98 transfer both old and new special + sec->oldspecial = line->frontsector->oldspecial; + break; + + case raiseToTexture: + { + int minsize = INT_MAX; + side_t* side; + + /* jff 3/13/98 no ovf */ + if (!comp[comp_model]) minsize = 32000<direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + for (i = 0; i < sec->linecount; i++) + { + if (twoSided (secnum, i) ) + { + side = getSide(secnum,i,0); + // jff 8/14/98 don't scan texture 0, its not real + if (side->bottomtexture > 0 || + (comp[comp_model] && !side->bottomtexture)) + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + side = getSide(secnum,i,1); + // jff 8/14/98 don't scan texture 0, its not real + if (side->bottomtexture > 0 || + (comp[comp_model] && !side->bottomtexture)) + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + } + } + if (comp[comp_model]) + floor->floordestheight = floor->sector->floorheight + minsize; + else + { + floor->floordestheight = + (floor->sector->floorheight>>FRACBITS) + (minsize>>FRACBITS); + if (floor->floordestheight>32000) + floor->floordestheight = 32000; //jff 3/13/98 do not + floor->floordestheight<<=FRACBITS; // allow height overflow + } + } + break; + + case lowerAndChange: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = P_FindLowestFloorSurrounding(sec); + floor->texture = sec->floorpic; + + // jff 1/24/98 make sure floor->newspecial gets initialized + // in case no surrounding sector is at floordestheight + // --> should not affect compatibility <-- + floor->newspecial = sec->special; + //jff 3/14/98 transfer both old and new special + floor->oldspecial = sec->oldspecial; + + //jff 5/23/98 use model subroutine to unify fixes and handling + sec = P_FindModelFloorSector(floor->floordestheight,sec->iSectorID); + if (sec) + { + floor->texture = sec->floorpic; + floor->newspecial = sec->special; + //jff 3/14/98 transfer both old and new special + floor->oldspecial = sec->oldspecial; + } + break; + default: + break; + } + if (zerotag_manual) return rtn; //e6y + } + return rtn; +} + +// +// EV_DoChange() +// +// Handle pure change types. These change floor texture and sector type +// by trigger or numeric model without moving the floor. +// +// The linedef causing the change and the type of change is passed +// Returns true if any sector changes +// +// jff 3/15/98 added to better support generalized sector types +// +int EV_DoChange +( line_t* line, + change_e changetype ) +{ + int secnum; + int rtn; + sector_t* sec; + sector_t* secm; + + secnum = -1; + rtn = 0; + // change all sectors with the same tag as the linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + + rtn = 1; + + // handle trigger or numeric change type + switch(changetype) + { + case trigChangeOnly: + sec->floorpic = line->frontsector->floorpic; + sec->special = line->frontsector->special; + sec->oldspecial = line->frontsector->oldspecial; + break; + case numChangeOnly: + secm = P_FindModelFloorSector(sec->floorheight,secnum); + if (secm) // if no model, no change + { + sec->floorpic = secm->floorpic; + sec->special = secm->special; + sec->oldspecial = secm->oldspecial; + } + break; + default: + break; + } + } + return rtn; +} + +/* + * EV_BuildStairs() + * + * Handles staircase building. A sequence of sectors chosen by algorithm + * rise at a speed indicated to a height that increases by the stepsize + * each step. + * + * Passed the linedef triggering the stairs and the type of stair rise + * Returns true if any thinkers are created + * + * cph 2001/09/21 - compatibility nightmares again + * There are three different ways this function has, during its history, stepped + * through all the stairs to be triggered by the single switch + * - original Doom used a linear P_FindSectorFromLineTag, but failed to preserve + * the index of the previous sector found, so instead it would restart its + * linear search from the last sector of the previous staircase + * - MBF/PrBoom with comp_stairs fail to emulate this, because their + * P_FindSectorFromLineTag is a chained hash table implementation. Instead they + * start following the hash chain from the last sector of the previous + * staircase, which will (probably) have the wrong tag, so they miss any further + * stairs + * - Boom fixed the bug, and MBF/PrBoom without comp_stairs work right + */ +static inline int P_FindSectorFromLineTagWithLowerBound +(line_t* l, int start, int min) +{ + /* Emulate original Doom's linear lower-bounded P_FindSectorFromLineTag + * as needed */ + do { + start = P_FindSectorFromLineTag(l,start); + } while (start >= 0 && start <= min); + return start; +} + +int EV_BuildStairs +( line_t* line, + stair_e type ) +{ + /* cph 2001/09/22 - cleaned up this function to save my sanity. A separate + * outer loop index makes the logic much cleared, and local variables moved + * into the inner blocks helps too */ + int ssec = -1; + int minssec = -1; + int rtn = 0; + + //e6y + int secnum = -1; + sector_t* sec; + if (ProcessNoTagLines(line, &sec, &secnum)) {if (zerotag_manual) goto manual_stair; else {return rtn;}};//e6y + + // start a stair at each sector tagged the same as the linedef + while ((ssec = P_FindSectorFromLineTagWithLowerBound(line,ssec,minssec)) >= 0) + { + //e6y int + secnum = ssec; + //e6y sector_t* + sec = §ors[secnum]; + +manual_stair://e6y + // don't start a stair if the first step's floor is already moving + if (!P_SectorActive(floor_special,sec)) { //jff 2/22/98 + floormove_t* floor; + int texture, height; + fixed_t stairsize; + fixed_t speed; + int ok; + + // create new floor thinker for first step + rtn = 1; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + sec->floordata = floor; + floor->thinker.function = T_MoveFloor; + floor->direction = 1; + floor->sector = sec; + floor->type = buildStair; //jff 3/31/98 do not leave uninited + + // set up the speed and stepsize according to the stairs type + switch(type) + { + default: // killough -- prevent compiler warning + case build8: + speed = FLOORSPEED/4; + stairsize = 8*FRACUNIT; + if (!demo_compatibility) + floor->crush = false; //jff 2/27/98 fix uninitialized crush field + // e6y + // Uninitialized crush field will not be equal to 0 or 1 (true) + // with high probability. So, initialize it with any other value + // There is no more desync on icarus.wad/ic29uv.lmp + // http://competn.doom2.net/pub/sda/i-o/icuvlmps.zip + // http://www.doomworld.com/idgames/index.php?id=5191 + else + { + if (!prboom_comp[PC_UNINITIALIZE_CRUSH_FIELD_FOR_STAIRS].state) + floor->crush = STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE; + } + + break; + case turbo16: + speed = FLOORSPEED*4; + stairsize = 16*FRACUNIT; + if (!demo_compatibility) + floor->crush = true; //jff 2/27/98 fix uninitialized crush field + // e6y + // Uninitialized crush field will not be equal to 0 or 1 (true) + // with high probability. So, initialize it with any other value + else + { + if (!prboom_comp[PC_UNINITIALIZE_CRUSH_FIELD_FOR_STAIRS].state) + floor->crush = STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE; + } + + break; + } + floor->speed = speed; + height = sec->floorheight + stairsize; + floor->floordestheight = height; + + texture = sec->floorpic; + + // Find next sector to raise + // 1. Find 2-sided line with same sector side[0] (lowest numbered) + // 2. Other side is the next sector to raise + // 3. Unless already moving, or different texture, then stop building + do + { + int i; + ok = 0; + + for (i = 0;i < sec->linecount;i++) + { + sector_t* tsec = (sec->lines[i])->frontsector; + int newsecnum; + if ( !((sec->lines[i])->flags & ML_TWOSIDED) ) + continue; + + newsecnum = tsec->iSectorID; + + if (secnum != newsecnum) + continue; + + tsec = (sec->lines[i])->backsector; + if (!tsec) continue; //jff 5/7/98 if no backside, continue + newsecnum = tsec->iSectorID; + + // if sector's floor is different texture, look for another + if (tsec->floorpic != texture) + continue; + + /* jff 6/19/98 prevent double stepsize + * killough 10/98: intentionally left this way [MBF comment] + * cph 2001/02/06: stair bug fix should be controlled by comp_stairs, + * except if we're emulating MBF which perversly reverted the fix + */ + if (comp[comp_stairs] || (compatibility_level == mbf_compatibility)) + height += stairsize; // jff 6/28/98 change demo compatibility + + // if sector's floor already moving, look for another + if (P_SectorActive(floor_special,tsec)) //jff 2/22/98 + continue; + + /* cph - see comment above - do this iff we didn't do so above */ + if (!comp[comp_stairs] && (compatibility_level != mbf_compatibility)) + height += stairsize; + + sec = tsec; + secnum = newsecnum; + + // create and initialize a thinker for the next step + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + + sec->floordata = floor; //jff 2/22/98 + floor->thinker.function = T_MoveFloor; + floor->direction = 1; + floor->sector = sec; + floor->speed = speed; + floor->floordestheight = height; + floor->type = buildStair; //jff 3/31/98 do not leave uninited + //jff 2/27/98 fix uninitialized crush field + if (!demo_compatibility) + floor->crush = type==build8? false : true; + // e6y + // Uninitialized crush field will not be equal to 0 or 1 (true) + // with high probability. So, initialize it with any other value + else + { + if (!prboom_comp[PC_UNINITIALIZE_CRUSH_FIELD_FOR_STAIRS].state) + floor->crush = STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE; + } + + ok = 1; + break; + } + } while(ok); // continue until no next step is found + + } + /* killough 10/98: compatibility option */ + if (comp[comp_stairs]) { + /* cph 2001/09/22 - emulate buggy MBF comp_stairs for demos, with logic + * reversed since we now have a separate outer loop index. + * DEMOSYNC - what about boom_compatibility_compatibility? + */ + if ((compatibility_level >= mbf_compatibility) && (compatibility_level < + prboom_3_compatibility)) ssec = secnum; /* Trash outer loop index */ + else { + /* cph 2001/09/22 - now the correct comp_stairs - Doom used a linear + * search from the last secnum, so we set that as a minimum value and do + * a fresh tag search + */ + ssec = -1; minssec = secnum; + } + } + if (zerotag_manual) return rtn; //e6y + } + return rtn; +} + +// +// EV_DoDonut() +// +// Handle donut function: lower pillar, raise surrounding pool, both to height, +// texture and type of the sector surrounding the pool. +// +// Passed the linedef that triggered the donut +// Returns whether a thinker was created +// +int EV_DoDonut(line_t* line) +{ + sector_t* s1; + sector_t* s2; + sector_t* s3; + int secnum; + int rtn; + int i; + floormove_t* floor; + + //e6y + fixed_t s3_floorheight; + short s3_floorpic; + + secnum = -1; + rtn = 0; + // do function on all sectors with same tag as linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + s1 = §ors[secnum]; // s1 is pillar's sector + + // do not start the donut if the pillar is already moving + if (P_SectorActive(floor_special,s1)) //jff 2/22/98 + continue; + + s2 = getNextSector(s1->lines[0],s1); // s2 is pool's sector + + // note lowest numbered line around + // pillar must be two-sided + if (!s2) + { + if (demo_compatibility) + { + lprintf(LO_ERROR, + "EV_DoDonut: lowest numbered line (linedef: %d) " + "around pillar (sector: %d) must be two-sided. " + "Unexpected behavior may occur in Vanilla Doom.\n", + s1->lines[0]->iLineID, s1->iSectorID); + continue; + } + else + { + continue; + } + } + + /* do not start the donut if the pool is already moving + * cph - DEMOSYNC - was !compatibility */ + if (!comp[comp_floors] && P_SectorActive(floor_special,s2)) + continue; //jff 5/7/98 + + // find a two sided line around the pool whose other side isn't the pillar + for (i = 0;i < s2->linecount;i++) + { + //jff 3/29/98 use true two-sidedness, not the flag + // killough 4/5/98: changed demo_compatibility to compatibility + if (comp[comp_model]) + { + // original code: !s2->lines[i]->flags & ML_TWOSIDED + // equivalent to: (!s2->lines[i]->flags) & ML_TWOSIDED , i.e. 0 + // should be: !(s2->lines[i]->flags & ML_TWOSIDED) + if (((!s2->lines[i]->flags) & ML_TWOSIDED) || + (s2->lines[i]->backsector == s1)) + continue; + } + else if (!s2->lines[i]->backsector || s2->lines[i]->backsector == s1) + continue; + + rtn = 1; //jff 1/26/98 no donut action - no switch change on return + + s3 = s2->lines[i]->backsector; // s3 is model sector for changes + + if (!s3) + { + // e6y + // s3->floorheight is an int at 0000:0000 + // s3->floorpic is a short at 0000:0008 + // Trying to emulate + lprintf(LO_ERROR, + "EV_DoDonut: Access violation at linedef %d, sector %d. " + "Unexpected behavior may occur in Vanilla Doom.\n", + line->iLineID, s1->iSectorID); + if (DonutOverrun(&s3_floorheight, &s3_floorpic)) + { + lprintf(LO_WARN, "EV_DoDonut: Emulated with floorheight %d, floor pic %d.\n", + s3_floorheight >> 16, s3_floorpic); + } + else + { + lprintf(LO_WARN, "EV_DoDonut: Not emulated.\n"); + break; + } + } + else + { + s3_floorheight = s3->floorheight; + s3_floorpic = s3->floorpic; + } + + // Spawn rising slime + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + s2->floordata = floor; //jff 2/22/98 + floor->thinker.function = T_MoveFloor; + floor->type = donutRaise; + floor->crush = false; + floor->direction = 1; + floor->sector = s2; + floor->speed = FLOORSPEED / 2; + floor->texture = s3_floorpic; + floor->newspecial = 0; + floor->floordestheight = s3_floorheight; + + // Spawn lowering donut-hole pillar + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + s1->floordata = floor; //jff 2/22/98 + floor->thinker.function = T_MoveFloor; + floor->type = lowerFloor; + floor->crush = false; + floor->direction = -1; + floor->sector = s1; + floor->speed = FLOORSPEED / 2; + floor->floordestheight = s3_floorheight; + break; + } + } + return rtn; +} + +// +// EV_DoElevator +// +// Handle elevator linedef types +// +// Passed the linedef that triggered the elevator and the elevator action +// +// jff 2/22/98 new type to move floor and ceiling in parallel +// +int EV_DoElevator +( line_t* line, + elevator_e elevtype ) +{ + int secnum; + int rtn; + sector_t* sec; + elevator_t* elevator; + + secnum = -1; + rtn = 0; + // act on all sectors with the same tag as the triggering linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + + // If either floor or ceiling is already activated, skip it + if (sec->floordata || sec->ceilingdata) //jff 2/22/98 + continue; + + // create and initialize new elevator thinker + rtn = 1; + elevator = Z_Malloc (sizeof(*elevator), PU_LEVSPEC, 0); + memset(elevator, 0, sizeof(*elevator)); + P_AddThinker (&elevator->thinker); + sec->floordata = elevator; //jff 2/22/98 + sec->ceilingdata = elevator; //jff 2/22/98 + elevator->thinker.function = T_MoveElevator; + elevator->type = elevtype; + + // set up the fields according to the type of elevator action + switch(elevtype) + { + // elevator down to next floor + case elevateDown: + elevator->direction = -1; + elevator->sector = sec; + elevator->speed = ELEVATORSPEED; + elevator->floordestheight = + P_FindNextLowestFloor(sec,sec->floorheight); + elevator->ceilingdestheight = + elevator->floordestheight + sec->ceilingheight - sec->floorheight; + break; + + // elevator up to next floor + case elevateUp: + elevator->direction = 1; + elevator->sector = sec; + elevator->speed = ELEVATORSPEED; + elevator->floordestheight = + P_FindNextHighestFloor(sec,sec->floorheight); + elevator->ceilingdestheight = + elevator->floordestheight + sec->ceilingheight - sec->floorheight; + break; + + // elevator to floor height of activating switch's front sector + case elevateCurrent: + elevator->sector = sec; + elevator->speed = ELEVATORSPEED; + elevator->floordestheight = line->frontsector->floorheight; + elevator->ceilingdestheight = + elevator->floordestheight + sec->ceilingheight - sec->floorheight; + elevator->direction = + elevator->floordestheight>sec->floorheight? 1 : -1; + break; + + default: + break; + } + } + return rtn; +} diff --git a/src/p_genlin.c b/src/p_genlin.c new file mode 100644 index 0000000..bc85bde --- /dev/null +++ b/src/p_genlin.c @@ -0,0 +1,1172 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Generalized linedef type handlers + * Floors, Ceilings, Doors, Locked Doors, Lifts, Stairs, Crushers + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" //jff 6/19/98 for demo_compatibility +#include "r_main.h" +#include "p_spec.h" +#include "p_tick.h" +#include "m_random.h" +#include "s_sound.h" +#include "sounds.h" +#include "e6y.h" + +////////////////////////////////////////////////////////// +// +// Generalized Linedef Type handlers +// +////////////////////////////////////////////////////////// + +// +// EV_DoGenFloor() +// +// Handle generalized floor types +// +// Passed the line activating the generalized floor function +// Returns true if a thinker is created +// +// jff 02/04/98 Added this routine (and file) to handle generalized +// floor movers using bit fields in the line special type. +// +int EV_DoGenFloor +( line_t* line ) +{ + int secnum; + int rtn; + dboolean manual; + sector_t* sec; + floormove_t* floor; + unsigned value = (unsigned)line->special - GenFloorBase; + + // parse the bit fields in the line's special type + + int Crsh = (value & FloorCrush) >> FloorCrushShift; + int ChgT = (value & FloorChange) >> FloorChangeShift; + int Targ = (value & FloorTarget) >> FloorTargetShift; + int Dirn = (value & FloorDirection) >> FloorDirectionShift; + int ChgM = (value & FloorModel) >> FloorModelShift; + int Sped = (value & FloorSpeed) >> FloorSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + rtn = 0; + + if (ProcessNoTagLines(line, &sec, &secnum)) {if (zerotag_manual) {manual = true; goto manual_floor;} else {return rtn;}};//e6y + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec->iSectorID; + manual = true; + goto manual_floor; + } + + secnum = -1; + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_floor: + // Do not start another function if floor already moving + if (P_SectorActive(floor_special,sec)) + { + if (!manual) + continue; + else + return rtn; + } + + // new floor thinker + rtn = 1; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + sec->floordata = floor; + floor->thinker.function = T_MoveFloor; + floor->crush = Crsh; + floor->direction = Dirn? 1 : -1; + floor->sector = sec; + floor->texture = sec->floorpic; + floor->newspecial = sec->special; + //jff 3/14/98 transfer old special field too + floor->oldspecial = sec->oldspecial; + floor->type = genFloor; + + // set the speed of motion + switch (Sped) + { + case SpeedSlow: + floor->speed = FLOORSPEED; + break; + case SpeedNormal: + floor->speed = FLOORSPEED*2; + break; + case SpeedFast: + floor->speed = FLOORSPEED*4; + break; + case SpeedTurbo: + floor->speed = FLOORSPEED*8; + break; + default: + break; + } + + // set the destination height + switch(Targ) + { + case FtoHnF: + floor->floordestheight = P_FindHighestFloorSurrounding(sec); + break; + case FtoLnF: + floor->floordestheight = P_FindLowestFloorSurrounding(sec); + break; + case FtoNnF: + floor->floordestheight = Dirn? + P_FindNextHighestFloor(sec,sec->floorheight) : + P_FindNextLowestFloor(sec,sec->floorheight); + break; + case FtoLnC: + floor->floordestheight = P_FindLowestCeilingSurrounding(sec); + break; + case FtoC: + floor->floordestheight = sec->ceilingheight; + break; + case FbyST: + floor->floordestheight = (floor->sector->floorheight>>FRACBITS) + + floor->direction * (P_FindShortestTextureAround(secnum)>>FRACBITS); + if (floor->floordestheight>32000) //jff 3/13/98 prevent overflow + floor->floordestheight=32000; // wraparound in floor height + if (floor->floordestheight<-32000) + floor->floordestheight=-32000; + floor->floordestheight<<=FRACBITS; + break; + case Fby24: + floor->floordestheight = floor->sector->floorheight + + floor->direction * 24*FRACUNIT; + break; + case Fby32: + floor->floordestheight = floor->sector->floorheight + + floor->direction * 32*FRACUNIT; + break; + default: + break; + } + + // set texture/type change properties + if (ChgT) // if a texture change is indicated + { + if (ChgM) // if a numeric model change + { + sector_t *sec; + + //jff 5/23/98 find model with ceiling at target height if target + //is a ceiling type + sec = (Targ==FtoLnC || Targ==FtoC)? + P_FindModelCeilingSector(floor->floordestheight,secnum) : + P_FindModelFloorSector(floor->floordestheight,secnum); + if (sec) + { + floor->texture = sec->floorpic; + switch(ChgT) + { + case FChgZero: // zero type + floor->newspecial = 0; + //jff 3/14/98 change old field too + floor->oldspecial = 0; + floor->type = genFloorChg0; + break; + case FChgTyp: // copy type + floor->newspecial = sec->special; + //jff 3/14/98 change old field too + floor->oldspecial = sec->oldspecial; + floor->type = genFloorChgT; + break; + case FChgTxt: // leave type be + floor->type = genFloorChg; + break; + default: + break; + } + } + } + else // else if a trigger model change + { + floor->texture = line->frontsector->floorpic; + switch (ChgT) + { + case FChgZero: // zero type + floor->newspecial = 0; + //jff 3/14/98 change old field too + floor->oldspecial = 0; + floor->type = genFloorChg0; + break; + case FChgTyp: // copy type + floor->newspecial = line->frontsector->special; + //jff 3/14/98 change old field too + floor->oldspecial = line->frontsector->oldspecial; + floor->type = genFloorChgT; + break; + case FChgTxt: // leave type be + floor->type = genFloorChg; + default: + break; + } + } + } + if (manual) return rtn; + } + return rtn; +} + + +// +// EV_DoGenCeiling() +// +// Handle generalized ceiling types +// +// Passed the linedef activating the ceiling function +// Returns true if a thinker created +// +// jff 02/04/98 Added this routine (and file) to handle generalized +// floor movers using bit fields in the line special type. +// +int EV_DoGenCeiling +( line_t* line ) +{ + int secnum; + int rtn; + dboolean manual; + fixed_t targheight; + sector_t* sec; + ceiling_t* ceiling; + unsigned value = (unsigned)line->special - GenCeilingBase; + + // parse the bit fields in the line's special type + + int Crsh = (value & CeilingCrush) >> CeilingCrushShift; + int ChgT = (value & CeilingChange) >> CeilingChangeShift; + int Targ = (value & CeilingTarget) >> CeilingTargetShift; + int Dirn = (value & CeilingDirection) >> CeilingDirectionShift; + int ChgM = (value & CeilingModel) >> CeilingModelShift; + int Sped = (value & CeilingSpeed) >> CeilingSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + rtn = 0; + + if (ProcessNoTagLines(line, &sec, &secnum)) {if (zerotag_manual) {manual = true; goto manual_ceiling;} else {return rtn;}};//e6y + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec->iSectorID; + manual = true; + goto manual_ceiling; + } + + secnum = -1; + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_ceiling: + // Do not start another function if ceiling already moving + if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 + { + if (!manual) + continue; + else + return rtn; + } + + // new ceiling thinker + rtn = 1; + ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVSPEC, 0); + memset(ceiling, 0, sizeof(*ceiling)); + P_AddThinker (&ceiling->thinker); + sec->ceilingdata = ceiling; //jff 2/22/98 + ceiling->thinker.function = T_MoveCeiling; + ceiling->crush = Crsh; + ceiling->direction = Dirn? 1 : -1; + ceiling->sector = sec; + ceiling->texture = sec->ceilingpic; + ceiling->newspecial = sec->special; + //jff 3/14/98 change old field too + ceiling->oldspecial = sec->oldspecial; + ceiling->tag = sec->tag; + ceiling->type = genCeiling; + + // set speed of motion + switch (Sped) + { + case SpeedSlow: + ceiling->speed = CEILSPEED; + break; + case SpeedNormal: + ceiling->speed = CEILSPEED*2; + break; + case SpeedFast: + ceiling->speed = CEILSPEED*4; + break; + case SpeedTurbo: + ceiling->speed = CEILSPEED*8; + break; + default: + break; + } + + // set destination target height + targheight = sec->ceilingheight; + switch(Targ) + { + case CtoHnC: + targheight = P_FindHighestCeilingSurrounding(sec); + break; + case CtoLnC: + targheight = P_FindLowestCeilingSurrounding(sec); + break; + case CtoNnC: + targheight = Dirn? + P_FindNextHighestCeiling(sec,sec->ceilingheight) : + P_FindNextLowestCeiling(sec,sec->ceilingheight); + break; + case CtoHnF: + targheight = P_FindHighestFloorSurrounding(sec); + break; + case CtoF: + targheight = sec->floorheight; + break; + case CbyST: + targheight = (ceiling->sector->ceilingheight>>FRACBITS) + + ceiling->direction * (P_FindShortestUpperAround(secnum)>>FRACBITS); + if (targheight>32000) //jff 3/13/98 prevent overflow + targheight=32000; // wraparound in ceiling height + if (targheight<-32000) + targheight=-32000; + targheight<<=FRACBITS; + break; + case Cby24: + targheight = ceiling->sector->ceilingheight + + ceiling->direction * 24*FRACUNIT; + break; + case Cby32: + targheight = ceiling->sector->ceilingheight + + ceiling->direction * 32*FRACUNIT; + break; + default: + break; + } + if (Dirn) ceiling->topheight = targheight; + else ceiling->bottomheight = targheight; + + // set texture/type change properties + if (ChgT) // if a texture change is indicated + { + if (ChgM) // if a numeric model change + { + sector_t *sec; + + //jff 5/23/98 find model with floor at target height if target + //is a floor type + sec = (Targ==CtoHnF || Targ==CtoF)? + P_FindModelFloorSector(targheight,secnum) : + P_FindModelCeilingSector(targheight,secnum); + if (sec) + { + ceiling->texture = sec->ceilingpic; + switch (ChgT) + { + case CChgZero: // type is zeroed + ceiling->newspecial = 0; + //jff 3/14/98 change old field too + ceiling->oldspecial = 0; + ceiling->type = genCeilingChg0; + break; + case CChgTyp: // type is copied + ceiling->newspecial = sec->special; + //jff 3/14/98 change old field too + ceiling->oldspecial = sec->oldspecial; + ceiling->type = genCeilingChgT; + break; + case CChgTxt: // type is left alone + ceiling->type = genCeilingChg; + break; + default: + break; + } + } + } + else // else if a trigger model change + { + ceiling->texture = line->frontsector->ceilingpic; + switch (ChgT) + { + case CChgZero: // type is zeroed + ceiling->newspecial = 0; + //jff 3/14/98 change old field too + ceiling->oldspecial = 0; + ceiling->type = genCeilingChg0; + break; + case CChgTyp: // type is copied + ceiling->newspecial = line->frontsector->special; + //jff 3/14/98 change old field too + ceiling->oldspecial = line->frontsector->oldspecial; + ceiling->type = genCeilingChgT; + break; + case CChgTxt: // type is left alone + ceiling->type = genCeilingChg; + break; + default: + break; + } + } + } + P_AddActiveCeiling(ceiling); // add this ceiling to the active list + if (manual) return rtn; + } + return rtn; +} + +// +// EV_DoGenLift() +// +// Handle generalized lift types +// +// Passed the linedef activating the lift +// Returns true if a thinker is created +// +int EV_DoGenLift +( line_t* line ) +{ + plat_t* plat; + int secnum; + int rtn; + dboolean manual; + sector_t* sec; + unsigned value = (unsigned)line->special - GenLiftBase; + + // parse the bit fields in the line's special type + + int Targ = (value & LiftTarget) >> LiftTargetShift; + int Dely = (value & LiftDelay) >> LiftDelayShift; + int Sped = (value & LiftSpeed) >> LiftSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + secnum = -1; + rtn = 0; + + // Activate all plats that are in_stasis + + if (Targ==LnF2HnF) + P_ActivateInStasis(line->tag); + + if (ProcessNoTagLines(line, &sec, &secnum)) {if (zerotag_manual) {manual = true; goto manual_lift;} else {return rtn;}};//e6y + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec->iSectorID; + manual = true; + goto manual_lift; + } + + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_lift: + // Do not start another function if floor already moving + if (P_SectorActive(floor_special,sec)) + { + if (!manual) + continue; + else + return rtn; + } + + // Setup the plat thinker + rtn = 1; + plat = Z_Malloc( sizeof(*plat), PU_LEVSPEC, 0); + memset(plat, 0, sizeof(*plat)); + P_AddThinker(&plat->thinker); + + plat->sector = sec; + plat->sector->floordata = plat; + plat->thinker.function = T_PlatRaise; + plat->crush = false; + plat->tag = line->tag; + + plat->type = genLift; + plat->high = sec->floorheight; + plat->status = down; + + // setup the target destination height + switch(Targ) + { + case F2LnF: + plat->low = P_FindLowestFloorSurrounding(sec); + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + break; + case F2NnF: + plat->low = P_FindNextLowestFloor(sec,sec->floorheight); + break; + case F2LnC: + plat->low = P_FindLowestCeilingSurrounding(sec); + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + break; + case LnF2HnF: + plat->type = genPerpetual; + plat->low = P_FindLowestFloorSurrounding(sec); + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + plat->high = P_FindHighestFloorSurrounding(sec); + if (plat->high < sec->floorheight) + plat->high = sec->floorheight; + plat->status = P_Random(pr_genlift)&1; + break; + default: + break; + } + + // setup the speed of motion + switch(Sped) + { + case SpeedSlow: + plat->speed = PLATSPEED * 2; + break; + case SpeedNormal: + plat->speed = PLATSPEED * 4; + break; + case SpeedFast: + plat->speed = PLATSPEED * 8; + break; + case SpeedTurbo: + plat->speed = PLATSPEED * 16; + break; + default: + break; + } + + // setup the delay time before the floor returns + switch(Dely) + { + case 0: + plat->wait = 1*35; + break; + case 1: + plat->wait = PLATWAIT*35; + break; + case 2: + plat->wait = 5*35; + break; + case 3: + plat->wait = 10*35; + break; + } + + S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart); + P_AddActivePlat(plat); // add this plat to the list of active plats + + if (manual) + return rtn; + } + return rtn; +} + +// +// EV_DoGenStairs() +// +// Handle generalized stair building +// +// Passed the linedef activating the stairs +// Returns true if a thinker is created +// +int EV_DoGenStairs +( line_t* line ) +{ + int secnum; + int osecnum; //jff 3/4/98 preserve loop index + int height; + int i; + int newsecnum; + int texture; + int ok; + int rtn; + dboolean manual; + + sector_t* sec; + sector_t* tsec; + + floormove_t* floor; + + fixed_t stairsize; + fixed_t speed; + + unsigned value = (unsigned)line->special - GenStairsBase; + + // parse the bit fields in the line's special type + + int Igno = (value & StairIgnore) >> StairIgnoreShift; + int Dirn = (value & StairDirection) >> StairDirectionShift; + int Step = (value & StairStep) >> StairStepShift; + int Sped = (value & StairSpeed) >> StairSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + rtn = 0; + + if (ProcessNoTagLines(line, &sec, &secnum)) {if (zerotag_manual) {manual = true; goto manual_stair;} else {return rtn;}};//e6y + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec->iSectorID; + manual = true; + goto manual_stair; + } + + secnum = -1; + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_stair: + //Do not start another function if floor already moving + //jff 2/26/98 add special lockout condition to wait for entire + //staircase to build before retriggering + if (P_SectorActive(floor_special,sec) || sec->stairlock) + { + if (!manual) + continue; + else + return rtn; + } + + // new floor thinker + rtn = 1; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + sec->floordata = floor; + floor->thinker.function = T_MoveFloor; + floor->direction = Dirn? 1 : -1; + floor->sector = sec; + + // setup speed of stair building + switch(Sped) + { + default: + case SpeedSlow: + floor->speed = FLOORSPEED/4; + break; + case SpeedNormal: + floor->speed = FLOORSPEED/2; + break; + case SpeedFast: + floor->speed = FLOORSPEED*2; + break; + case SpeedTurbo: + floor->speed = FLOORSPEED*4; + break; + } + + // setup stepsize for stairs + switch(Step) + { + default: + case 0: + stairsize = 4*FRACUNIT; + break; + case 1: + stairsize = 8*FRACUNIT; + break; + case 2: + stairsize = 16*FRACUNIT; + break; + case 3: + stairsize = 24*FRACUNIT; + break; + } + + speed = floor->speed; + height = sec->floorheight + floor->direction * stairsize; + floor->floordestheight = height; + texture = sec->floorpic; + floor->crush = false; + floor->type = genBuildStair; // jff 3/31/98 do not leave uninited + + sec->stairlock = -2; // jff 2/26/98 set up lock on current sector + sec->nextsec = -1; + sec->prevsec = -1; + + osecnum = secnum; //jff 3/4/98 preserve loop index + // Find next sector to raise + // 1. Find 2-sided line with same sector side[0] + // 2. Other side is the next sector to raise + do + { + ok = 0; + for (i = 0;i < sec->linecount;i++) + { + if ( !((sec->lines[i])->backsector) ) + continue; + + tsec = (sec->lines[i])->frontsector; + newsecnum = tsec->iSectorID; + + if (secnum != newsecnum) + continue; + + tsec = (sec->lines[i])->backsector; + newsecnum = tsec->iSectorID; + + if (!Igno && tsec->floorpic != texture) + continue; + + /* jff 6/19/98 prevent double stepsize */ + if (compatibility_level < boom_202_compatibility) + height += floor->direction * stairsize; + + //jff 2/26/98 special lockout condition for retriggering + if (P_SectorActive(floor_special,tsec) || tsec->stairlock) + continue; + + /* jff 6/19/98 increase height AFTER continue */ + if (compatibility_level >= boom_202_compatibility) + height += floor->direction * stairsize; + + // jff 2/26/98 + // link the stair chain in both directions + // lock the stair sector until building complete + sec->nextsec = newsecnum; // link step to next + tsec->prevsec = secnum; // link next back + tsec->nextsec = -1; // set next forward link as end + tsec->stairlock = -2; // lock the step + + sec = tsec; + secnum = newsecnum; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + + sec->floordata = floor; + floor->thinker.function = T_MoveFloor; + floor->direction = Dirn? 1 : -1; + floor->sector = sec; + floor->speed = speed; + floor->floordestheight = height; + floor->crush = false; + floor->type = genBuildStair; // jff 3/31/98 do not leave uninited + + ok = 1; + break; + } + } while(ok); + if (manual) + return rtn; + secnum = osecnum; //jff 3/4/98 restore old loop index + } + // retriggerable generalized stairs build up or down alternately + if (rtn) + line->special ^= StairDirection; // alternate dir on succ activations + return rtn; +} + +// +// EV_DoGenCrusher() +// +// Handle generalized crusher types +// +// Passed the linedef activating the crusher +// Returns true if a thinker created +// +int EV_DoGenCrusher +( line_t* line ) +{ + int secnum; + int rtn; + dboolean manual; + sector_t* sec; + ceiling_t* ceiling; + unsigned value = (unsigned)line->special - GenCrusherBase; + + // parse the bit fields in the line's special type + + int Slnt = (value & CrusherSilent) >> CrusherSilentShift; + int Sped = (value & CrusherSpeed) >> CrusherSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + //jff 2/22/98 Reactivate in-stasis ceilings...for certain types. + //jff 4/5/98 return if activated + rtn = P_ActivateInStasisCeiling(line); + + if (ProcessNoTagLines(line, &sec, &secnum)) {if (zerotag_manual) {manual = true; goto manual_crusher;} else {return rtn;}};//e6y + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec->iSectorID; + manual = true; + goto manual_crusher; + } + + secnum = -1; + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_crusher: + // Do not start another function if ceiling already moving + if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 + { + if (!manual) + continue; + else + return rtn; + } + + // new ceiling thinker + rtn = 1; + ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVSPEC, 0); + memset(ceiling, 0, sizeof(*ceiling)); + P_AddThinker (&ceiling->thinker); + sec->ceilingdata = ceiling; //jff 2/22/98 + ceiling->thinker.function = T_MoveCeiling; + ceiling->crush = true; + ceiling->direction = -1; + ceiling->sector = sec; + ceiling->texture = sec->ceilingpic; + ceiling->newspecial = sec->special; + ceiling->tag = sec->tag; + ceiling->type = Slnt? genSilentCrusher : genCrusher; + ceiling->topheight = sec->ceilingheight; + ceiling->bottomheight = sec->floorheight + (8*FRACUNIT); + + // setup ceiling motion speed + switch (Sped) + { + case SpeedSlow: + ceiling->speed = CEILSPEED; + break; + case SpeedNormal: + ceiling->speed = CEILSPEED*2; + break; + case SpeedFast: + ceiling->speed = CEILSPEED*4; + break; + case SpeedTurbo: + ceiling->speed = CEILSPEED*8; + break; + default: + break; + } + ceiling->oldspeed=ceiling->speed; + + P_AddActiveCeiling(ceiling); // add to list of active ceilings + if (manual) return rtn; + } + return rtn; +} + +// +// EV_DoGenLockedDoor() +// +// Handle generalized locked door types +// +// Passed the linedef activating the generalized locked door +// Returns true if a thinker created +// +int EV_DoGenLockedDoor +( line_t* line ) +{ + int secnum,rtn; + sector_t* sec; + vldoor_t* door; + dboolean manual; + unsigned value = (unsigned)line->special - GenLockedBase; + + // parse the bit fields in the line's special type + + int Kind = (value & LockedKind) >> LockedKindShift; + int Sped = (value & LockedSpeed) >> LockedSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + rtn = 0; + + if (ProcessNoTagLines(line, &sec, &secnum)) {if (zerotag_manual) {manual = true; goto manual_locked;} else {return rtn;}};//e6y + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec->iSectorID; + manual = true; + goto manual_locked; + } + + secnum = -1; + rtn = 0; + + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; +manual_locked: + // Do not start another function if ceiling already moving + if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 + { + if (!manual) + continue; + else + return rtn; + } + + // new door thinker + rtn = 1; + door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + sec->ceilingdata = door; //jff 2/22/98 + + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->topwait = VDOORWAIT; + door->line = line; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->direction = 1; + + /* killough 10/98: implement gradual lighting */ + door->lighttag = !comp[comp_doorlight] && + (line->special&6) == 6 && + line->special > GenLockedBase ? line->tag : 0; + + // setup speed of door motion + switch(Sped) + { + default: + case SpeedSlow: + door->type = Kind? genOpen : genRaise; + door->speed = VDOORSPEED; + break; + case SpeedNormal: + door->type = Kind? genOpen : genRaise; + door->speed = VDOORSPEED*2; + break; + case SpeedFast: + door->type = Kind? genBlazeOpen : genBlazeRaise; + door->speed = VDOORSPEED*4; + break; + case SpeedTurbo: + door->type = Kind? genBlazeOpen : genBlazeRaise; + door->speed = VDOORSPEED*8; + + break; + } + + // killough 4/15/98: fix generalized door opening sounds + // (previously they always had the blazing door close sound) + + S_StartSound((mobj_t *)&door->sector->soundorg, // killough 4/15/98 + door->speed >= VDOORSPEED*4 ? sfx_bdopn : sfx_doropn); + + if (manual) + return rtn; + } + return rtn; +} + +// +// EV_DoGenDoor() +// +// Handle generalized door types +// +// Passed the linedef activating the generalized door +// Returns true if a thinker created +// +int EV_DoGenDoor +( line_t* line ) +{ + int secnum,rtn; + sector_t* sec; + dboolean manual; + vldoor_t* door; + unsigned value = (unsigned)line->special - GenDoorBase; + + // parse the bit fields in the line's special type + + int Dely = (value & DoorDelay) >> DoorDelayShift; + int Kind = (value & DoorKind) >> DoorKindShift; + int Sped = (value & DoorSpeed) >> DoorSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + rtn = 0; + + if (ProcessNoTagLines(line, &sec, &secnum)) {if (zerotag_manual) {manual = true; goto manual_door;} else {return rtn;}};//e6y + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec->iSectorID; + manual = true; + goto manual_door; + } + + + secnum = -1; + rtn = 0; + + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; +manual_door: + // Do not start another function if ceiling already moving + if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 + { + if (!manual) + continue; + else + return rtn; + } + + // new door thinker + rtn = 1; + door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + sec->ceilingdata = door; //jff 2/22/98 + + door->thinker.function = T_VerticalDoor; + door->sector = sec; + // setup delay for door remaining open/closed + switch(Dely) + { + default: + case 0: + door->topwait = 35; + break; + case 1: + door->topwait = VDOORWAIT; + break; + case 2: + door->topwait = 2*VDOORWAIT; + break; + case 3: + door->topwait = 7*VDOORWAIT; + break; + } + + // setup speed of door motion + switch(Sped) + { + default: + case SpeedSlow: + door->speed = VDOORSPEED; + break; + case SpeedNormal: + door->speed = VDOORSPEED*2; + break; + case SpeedFast: + door->speed = VDOORSPEED*4; + break; + case SpeedTurbo: + door->speed = VDOORSPEED*8; + break; + } + door->line = line; // jff 1/31/98 remember line that triggered us + + /* killough 10/98: implement gradual lighting */ + door->lighttag = !comp[comp_doorlight] && + (line->special&6) == 6 && + line->special > GenLockedBase ? line->tag : 0; + + // set kind of door, whether it opens then close, opens, closes etc. + // assign target heights accordingly + switch(Kind) + { + case OdCDoor: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + if (door->topheight != sec->ceilingheight) + S_StartSound((mobj_t *)&door->sector->soundorg,Sped>=SpeedFast || default_comp[comp_sound] ? sfx_bdopn : sfx_doropn); + door->type = Sped>=SpeedFast? genBlazeRaise : genRaise; + break; + case ODoor: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + if (door->topheight != sec->ceilingheight) + S_StartSound((mobj_t *)&door->sector->soundorg,Sped>=SpeedFast || default_comp[comp_sound] ? sfx_bdopn : sfx_doropn); + door->type = Sped>=SpeedFast? genBlazeOpen : genOpen; + break; + case CdODoor: + door->topheight = sec->ceilingheight; + door->direction = -1; + S_StartSound((mobj_t *)&door->sector->soundorg,Sped>=SpeedFast && !default_comp[comp_sound] ? sfx_bdcls : sfx_dorcls); + door->type = Sped>=SpeedFast? genBlazeCdO : genCdO; + break; + case CDoor: + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->direction = -1; + S_StartSound((mobj_t *)&door->sector->soundorg,Sped>=SpeedFast && !default_comp[comp_sound] ? sfx_bdcls : sfx_dorcls); + door->type = Sped>=SpeedFast? genBlazeClose : genClose; + break; + default: + break; + } + if (manual) + return rtn; + } + return rtn; +} diff --git a/src/p_inter.c b/src/p_inter.c new file mode 100644 index 0000000..ed661d9 --- /dev/null +++ b/src/p_inter.c @@ -0,0 +1,1027 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Handling interactions (i.e., collisions). + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "dstrings.h" +#include "m_random.h" +#include "am_map.h" +#include "r_main.h" +#include "s_sound.h" +#include "sounds.h" +#include "d_deh.h" // Ty 03/22/98 - externalized strings +#include "p_tick.h" +#include "lprintf.h" + +#include "p_inter.h" +#include "p_enemy.h" +#include "hu_tracers.h" + +#ifdef __GNUG__ +#pragma implementation "p_inter.h" +#endif +#include "p_inter.h" +#include "e6y.h"//e6y + +#define BONUSADD 6 + +// Ty 03/07/98 - add deh externals +// Maximums and such were hardcoded values. Need to externalize those for +// dehacked support (and future flexibility). Most var names came from the key +// strings used in dehacked. + +int initial_health = 100; +int initial_bullets = 50; +int maxhealth = 100; // was MAXHEALTH as a #define, used only in this module +int maxhealthbonus = 200; +int max_armor = 200; +int green_armor_class = 1; // these are involved with armortype below +int blue_armor_class = 2; +int max_soul = 200; +int soul_health = 100; +int mega_health = 200; +int god_health = 100; // these are used in cheats (see st_stuff.c) +int idfa_armor = 200; +int idfa_armor_class = 2; +// not actually used due to pairing of cheat_k and cheat_fa +int idkfa_armor = 200; +int idkfa_armor_class = 2; + +int bfgcells = 40; // used in p_pspr.c +int monsters_infight = 0; // e6y: Dehacked support - monsters infight +// Ty 03/07/98 - end deh externals + +// a weapon is found with two clip loads, +// a big item has five clip loads +int maxammo[NUMAMMO] = {200, 50, 300, 50}; +int clipammo[NUMAMMO] = { 10, 4, 20, 1}; + +// +// GET STUFF +// + +// +// P_GiveAmmo +// Num is the number of clip loads, +// not the individual count (0= 1/2 clip). +// Returns false if the ammo can't be picked up at all +// + +static dboolean P_GiveAmmo(player_t *player, ammotype_t ammo, int num) +{ + int oldammo; + + if (ammo == am_noammo) + return false; + +#ifdef RANGECHECK + if (ammo < 0 || ammo > NUMAMMO) + I_Error ("P_GiveAmmo: bad type %i", ammo); +#endif + + if ( player->ammo[ammo] == player->maxammo[ammo] ) + return false; + + if (num) + num *= clipammo[ammo]; + else + num = clipammo[ammo]/2; + + // give double ammo in trainer mode, you'll need in nightmare + if (gameskill == sk_baby || gameskill == sk_nightmare) + num <<= 1; + + oldammo = player->ammo[ammo]; + player->ammo[ammo] += num; + + if (player->ammo[ammo] > player->maxammo[ammo]) + player->ammo[ammo] = player->maxammo[ammo]; + + // If non zero ammo, don't change up weapons, player was lower on purpose. + if (oldammo) + return true; + + // We were down to zero, so select a new weapon. + // Preferences are not user selectable. + + switch (ammo) + { + case am_clip: + if (player->readyweapon == wp_fist) { + if (player->weaponowned[wp_chaingun]) + player->pendingweapon = wp_chaingun; + else + player->pendingweapon = wp_pistol; + } + break; + + case am_shell: + if (player->readyweapon == wp_fist || player->readyweapon == wp_pistol) + if (player->weaponowned[wp_shotgun]) + player->pendingweapon = wp_shotgun; + break; + + case am_cell: + if (player->readyweapon == wp_fist || player->readyweapon == wp_pistol) + if (player->weaponowned[wp_plasma]) + player->pendingweapon = wp_plasma; + break; + + case am_misl: + if (player->readyweapon == wp_fist) + if (player->weaponowned[wp_missile]) + player->pendingweapon = wp_missile; + default: + break; + } + return true; +} + +// +// P_GiveWeapon +// The weapon name may have a MF_DROPPED flag ored in. +// + +static dboolean P_GiveWeapon(player_t *player, weapontype_t weapon, dboolean dropped) +{ + dboolean gaveammo; + dboolean 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; + + P_GiveAmmo(player, weaponinfo[weapon].ammo, deathmatch ? 5 : 2); + + player->pendingweapon = weapon; + /* cph 20028/10 - for old-school DM addicts, allow old behavior + * where only consoleplayer's pickup sounds are heard */ + // displayplayer, not consoleplayer, for viewing multiplayer demos + if (!default_comp[comp_sound] || player == &players[displayplayer]) + S_StartSound (player->mo, sfx_wpnup|PICKUP_SOUND); // killough 4/25/98 + return false; + } + + if (weaponinfo[weapon].ammo != am_noammo) + { + // give one clip with a dropped weapon, + // two clips with a found weapon + gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, dropped ? 1 : 2); + } + else + gaveammo = false; + + if (player->weaponowned[weapon]) + gaveweapon = false; + else + { + gaveweapon = true; + player->weaponowned[weapon] = true; + player->pendingweapon = weapon; + } + return gaveweapon || gaveammo; +} + +// +// P_GiveBody +// Returns false if the body isn't needed at all +// + +static dboolean P_GiveBody(player_t *player, int num) +{ + if (player->health >= maxhealth) + return false; // Ty 03/09/98 externalized MAXHEALTH to maxhealth + player->health += num; + if (player->health > maxhealth) + player->health = maxhealth; + player->mo->health = player->health; + return true; +} + +// +// P_GiveArmor +// Returns false if the armor is worse +// than the current armor. +// + +static dboolean P_GiveArmor(player_t *player, int armortype) +{ + int hits = armortype*100; + if (player->armorpoints >= hits) + return false; // don't pick up + player->armortype = armortype; + player->armorpoints = hits; + return true; +} + +// +// P_GiveCard +// + +static void P_GiveCard(player_t *player, card_t card) +{ + if (player->cards[card]) + return; + player->bonuscount = BONUSADD; + player->cards[card] = 1; +} + +// +// P_GivePower +// +// Rewritten by Lee Killough +// + +dboolean P_GivePower(player_t *player, int power) +{ + static const int tics[NUMPOWERS] = { + INVULNTICS, 1 /* strength */, INVISTICS, + IRONTICS, 1 /* allmap */, INFRATICS, + }; + + switch (power) + { + case pw_invisibility: + player->mo->flags |= MF_SHADOW; + break; + case pw_allmap: + if (player->powers[pw_allmap]) + return false; + break; + case pw_strength: + P_GiveBody(player,100); + break; + } + + // Unless player has infinite duration cheat, set duration (killough) + + if (player->powers[power] >= 0) + player->powers[power] = tics[power]; + return true; +} + +// +// P_TouchSpecialThing +// + +void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher) +{ + player_t *player; + int i; + int sound; + fixed_t delta = special->z - toucher->z; + + if (delta > toucher->height || delta < -8*FRACUNIT) + return; // out of reach + + sound = sfx_itemup; + player = toucher->player; + + // Dead thing touching. + // Can happen with a sliding player corpse. + if (toucher->health <= 0) + return; + + // Identify by sprite. + switch (special->sprite) + { + // armor + case SPR_ARM1: + if (!P_GiveArmor (player, green_armor_class)) + return; + player->message = s_GOTARMOR; // Ty 03/22/98 - externalized + break; + + case SPR_ARM2: + if (!P_GiveArmor (player, blue_armor_class)) + return; + player->message = s_GOTMEGA; // Ty 03/22/98 - externalized + break; + + // bonus items + case SPR_BON1: + player->health++; // can go over 100% + if (player->health > (maxhealthbonus))//e6y + player->health = (maxhealthbonus);//e6y + player->mo->health = player->health; + player->message = s_GOTHTHBONUS; // Ty 03/22/98 - externalized + break; + + case SPR_BON2: + player->armorpoints++; // can go over 100% + // e6y + // Doom 1.2 does not do check of armor points on overflow. + // If you set the "IDKFA Armor" to MAX_INT (DWORD at 0x00064B5A -> FFFFFF7F) + // and pick up one or more armor bonuses, your armor becomes negative + // and you will die after reception of any damage since this moment. + // It happens because the taken health damage depends from armor points + // if they are present and becomes equal to very large value in this case + if (player->armorpoints > max_armor && compatibility_level != doom_12_compatibility) + player->armorpoints = max_armor; + // e6y + // We always give armor type 1 for the armor bonuses; + // dehacked only affects the GreenArmor. + if (!player->armortype) + player->armortype = + ((!demo_compatibility || prboom_comp[PC_APPLY_GREEN_ARMOR_CLASS_TO_ARMOR_BONUSES].state) ? + green_armor_class : 1); + player->message = s_GOTARMBONUS; // Ty 03/22/98 - externalized + break; + + case SPR_SOUL: + player->health += soul_health; + if (player->health > max_soul) + player->health = max_soul; + player->mo->health = player->health; + player->message = s_GOTSUPER; // Ty 03/22/98 - externalized + if (compatibility_level > doom_12_compatibility) + sound = sfx_getpow; + break; + + case SPR_MEGA: + if (gamemode != commercial) + return; + player->health = mega_health; + player->mo->health = player->health; + // e6y + // We always give armor type 2 for the megasphere; + // dehacked only affects the MegaArmor. + P_GiveArmor (player, + ((!demo_compatibility || prboom_comp[PC_APPLY_BLUE_ARMOR_CLASS_TO_MEGASPHERE].state) ? + blue_armor_class : 2)); + player->message = s_GOTMSPHERE; // Ty 03/22/98 - externalized + sound = sfx_getpow; + break; + + // cards + // leave cards for everyone + case SPR_BKEY: + if (!player->cards[it_bluecard]) + player->message = s_GOTBLUECARD; // Ty 03/22/98 - externalized + P_GiveCard (player, it_bluecard); + if (!netgame) + break; + return; + + case SPR_YKEY: + if (!player->cards[it_yellowcard]) + player->message = s_GOTYELWCARD; // Ty 03/22/98 - externalized + P_GiveCard (player, it_yellowcard); + if (!netgame) + break; + return; + + case SPR_RKEY: + if (!player->cards[it_redcard]) + player->message = s_GOTREDCARD; // Ty 03/22/98 - externalized + P_GiveCard (player, it_redcard); + if (!netgame) + break; + return; + + case SPR_BSKU: + if (!player->cards[it_blueskull]) + player->message = s_GOTBLUESKUL; // Ty 03/22/98 - externalized + P_GiveCard (player, it_blueskull); + if (!netgame) + break; + return; + + case SPR_YSKU: + if (!player->cards[it_yellowskull]) + player->message = s_GOTYELWSKUL; // Ty 03/22/98 - externalized + P_GiveCard (player, it_yellowskull); + if (!netgame) + break; + return; + + case SPR_RSKU: + if (!player->cards[it_redskull]) + player->message = s_GOTREDSKULL; // Ty 03/22/98 - externalized + P_GiveCard (player, it_redskull); + if (!netgame) + break; + return; + + // medikits, heals + case SPR_STIM: + if (!P_GiveBody (player, 10)) + return; + player->message = s_GOTSTIM; // Ty 03/22/98 - externalized + break; + + case SPR_MEDI: + if (!P_GiveBody (player, 25)) + return; + + if (player->health < 50) // cph - 25 + the 25 just added, thanks to Quasar for reporting this bug + player->message = s_GOTMEDINEED; // Ty 03/22/98 - externalized + else + player->message = s_GOTMEDIKIT; // Ty 03/22/98 - externalized + break; + + + // power ups + case SPR_PINV: + if (!P_GivePower (player, pw_invulnerability)) + return; + player->message = s_GOTINVUL; // Ty 03/22/98 - externalized + if (compatibility_level > doom_12_compatibility) + sound = sfx_getpow; + break; + + case SPR_PSTR: + if (!P_GivePower (player, pw_strength)) + return; + player->message = s_GOTBERSERK; // Ty 03/22/98 - externalized + if (player->readyweapon != wp_fist) + player->pendingweapon = wp_fist; + if (compatibility_level > doom_12_compatibility) + sound = sfx_getpow; + break; + + case SPR_PINS: + if (!P_GivePower (player, pw_invisibility)) + return; + player->message = s_GOTINVIS; // Ty 03/22/98 - externalized + if (compatibility_level > doom_12_compatibility) + sound = sfx_getpow; + break; + + case SPR_SUIT: + if (!P_GivePower (player, pw_ironfeet)) + return; + player->message = s_GOTSUIT; // Ty 03/22/98 - externalized + if (compatibility_level > doom_12_compatibility) + sound = sfx_getpow; + break; + + case SPR_PMAP: + if (!P_GivePower (player, pw_allmap)) + return; + player->message = s_GOTMAP; // Ty 03/22/98 - externalized + if (compatibility_level > doom_12_compatibility) + sound = sfx_getpow; + break; + + case SPR_PVIS: + if (!P_GivePower (player, pw_infrared)) + return; + player->message = s_GOTVISOR; // Ty 03/22/98 - externalized + if (compatibility_level > doom_12_compatibility) + sound = sfx_getpow; + break; + + // ammo + case SPR_CLIP: + if (special->flags & MF_DROPPED) + { + if (!P_GiveAmmo (player,am_clip,0)) + return; + } + else + { + if (!P_GiveAmmo (player,am_clip,1)) + return; + } + player->message = s_GOTCLIP; // Ty 03/22/98 - externalized + break; + + case SPR_AMMO: + if (!P_GiveAmmo (player, am_clip,5)) + return; + player->message = s_GOTCLIPBOX; // Ty 03/22/98 - externalized + break; + + case SPR_ROCK: + if (!P_GiveAmmo (player, am_misl,1)) + return; + player->message = s_GOTROCKET; // Ty 03/22/98 - externalized + break; + + case SPR_BROK: + if (!P_GiveAmmo (player, am_misl,5)) + return; + player->message = s_GOTROCKBOX; // Ty 03/22/98 - externalized + break; + + case SPR_CELL: + if (!P_GiveAmmo (player, am_cell,1)) + return; + player->message = s_GOTCELL; // Ty 03/22/98 - externalized + break; + + case SPR_CELP: + if (!P_GiveAmmo (player, am_cell,5)) + return; + player->message = s_GOTCELLBOX; // Ty 03/22/98 - externalized + break; + + case SPR_SHEL: + if (!P_GiveAmmo (player, am_shell,1)) + return; + player->message = s_GOTSHELLS; // Ty 03/22/98 - externalized + break; + + case SPR_SBOX: + if (!P_GiveAmmo (player, am_shell,5)) + return; + player->message = s_GOTSHELLBOX; // Ty 03/22/98 - externalized + break; + + case SPR_BPAK: + if (!player->backpack) + { + for (i=0 ; imaxammo[i] *= 2; + player->backpack = true; + } + for (i=0 ; imessage = s_GOTBACKPACK; // Ty 03/22/98 - externalized + break; + + // weapons + case SPR_BFUG: + if (!P_GiveWeapon (player, wp_bfg, false) ) + return; + player->message = s_GOTBFG9000; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_MGUN: + if (!P_GiveWeapon (player, wp_chaingun, (special->flags&MF_DROPPED)!=0) ) + return; + player->message = s_GOTCHAINGUN; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_CSAW: + if (!P_GiveWeapon (player, wp_chainsaw, false) ) + return; + player->message = s_GOTCHAINSAW; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_LAUN: + if (!P_GiveWeapon (player, wp_missile, false) ) + return; + player->message = s_GOTLAUNCHER; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_PLAS: + if (!P_GiveWeapon (player, wp_plasma, false) ) + return; + player->message = s_GOTPLASMA; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_SHOT: + if (!P_GiveWeapon (player, wp_shotgun, (special->flags&MF_DROPPED)!=0 ) ) + return; + player->message = s_GOTSHOTGUN; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_SGN2: + if (!P_GiveWeapon(player, wp_supershotgun, (special->flags&MF_DROPPED)!=0)) + return; + player->message = s_GOTSHOTGUN2; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + default: + I_Error ("P_SpecialThing: Unknown gettable thing"); + } + + if (special->flags & MF_COUNTITEM) + player->itemcount++; + P_RemoveMobj (special); + player->bonuscount += BONUSADD; + + CheckThingsPickupTracer(special);//e6y + + /* cph 20028/10 - for old-school DM addicts, allow old behavior + * where only consoleplayer's pickup sounds are heard */ + // displayplayer, not consoleplayer, for viewing multiplayer demos + if (!default_comp[comp_sound] || player == &players[displayplayer]) + S_StartSound (player->mo, sound | PICKUP_SOUND); // killough 4/25/98 +} + +// +// KillMobj +// +// killough 11/98: make static +static void P_KillMobj(mobj_t *source, mobj_t *target) +{ + mobjtype_t item; + mobj_t *mo; + dboolean e6y = false; + +#if 0 + if (target->player && source && target->health < -target->info->spawnhealth && + !demorecording && !demoplayback) + { + angle_t ang = R_PointToAngle2(target->x, target->y, source->x, source->y) - target->angle; + e6y = (ang > (unsigned)(ANG180 - ANG45) && ang < (unsigned)(ANG180 + ANG45)); + } +#endif + + 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 (compatibility_level == mbf_compatibility && + !prboom_comp[PC_MBF_REMOVE_THINKER_IN_KILLMOBJ].state) + { + // killough 8/29/98: remove from threaded list + P_UpdateThinker(&target->thinker); + } + + if (!((target->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) + totallive--; + + if (source && source->player) + { + // count for intermission + if (target->flags & MF_COUNTKILL) + { + source->player->killcount++; + + if (target->flags & MF_RESSURECTED) + source->player->resurectedkillcount++; + } + if (target->player) + source->player->frags[target->player-players]++; + } + else + if (target->flags & MF_COUNTKILL) { /* Add to kills tally */ + if ((compatibility_level < lxdoom_1_compatibility) || !netgame) { + if (!netgame) + { + // count all monster deaths, + // even those caused by other monsters + players[0].killcount++; + + if (target->flags & MF_RESSURECTED) + players[0].resurectedkillcount++; + } + else + { + if (!deathmatch) { + if (target->lastenemy && target->lastenemy->health > 0 && target->lastenemy->player) + { + target->lastenemy->player->killcount++; + if (target->flags & MF_RESSURECTED) + target->lastenemy->player->resurectedkillcount++; + } + else + { + unsigned int player; + for (player = 0; playerflags & MF_RESSURECTED) + players[player].resurectedkillcount++; + break; + } + } + } + } + } + + } else + if (!deathmatch) { + // try and find a player to give the kill to, otherwise give the + // kill to a random player. this fixes the missing monsters bug + // in coop - rain + // CPhipps - not a bug as such, but certainly an inconsistency. + if (target->lastenemy && target->lastenemy->health > 0 + && target->lastenemy->player) // Fighting a player + { + target->lastenemy->player->killcount++; + + if (target->flags & MF_RESSURECTED) + target->lastenemy->player->resurectedkillcount++; + } + else { + // cph - randomely choose a player in the game to be credited + // and do it uniformly between the active players + unsigned int activeplayers = 0, player, i; + + for (player = 0; playerflags & MF_RESSURECTED) + players[i].resurectedkillcount++; + } + } + } + } + } + + if (target->player) + { + // count environment kills against you + if (!source) + target->player->frags[target->player-players]++; + + target->flags &= ~MF_SOLID; + target->player->playerstate = PST_DEAD; + P_DropWeapon (target->player); + + if (target->player == &players[consoleplayer] && (automapmode & am_active)) + AM_Stop(); // don't die in auto map; switch view prior to dying + } + + if (e6y) + { + P_SetMobjState (target, S_PLAY_GDIE1); + } + else + { + if (target->health < -target->info->spawnhealth && target->info->xdeathstate) + P_SetMobjState (target, target->info->xdeathstate); + else + P_SetMobjState (target, target->info->deathstate); + } + + target->tics -= P_Random(pr_killtics)&3; + + if (target->tics < 1) + target->tics = 1; + + // In Chex Quest, monsters don't drop items. + if (gamemission == chex) + { + return; + } + + // Drop stuff. + // This determines the kind of object spawned + // during the death frame of a thing. + + if (target->info->droppeditem != MT_NULL) + { + item = target->info->droppeditem; + } + else return; + + mo = P_SpawnMobj (target->x,target->y,ONFLOORZ, item); + mo->flags |= MF_DROPPED; // special versions of items + +#ifdef GL_DOOM + if (target->momx == 0 && target->momy == 0) + { + target->flags |= MF_FOREGROUND; + } +#endif +} + +// +// P_DamageMobj +// 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_DamageMobj(mobj_t *target,mobj_t *inflictor, mobj_t *source, int damage) +{ + player_t *player; + dboolean justhit = false; /* killough 11/98 */ + + /* killough 8/31/98: allow bouncers to take damage */ + if (!(target->flags & (MF_SHOOTABLE | MF_BOUNCES))) + 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)) + { + unsigned ang = R_PointToAngle2 (inflictor->x, inflictor->y, + target->x, target->y); + + fixed_t 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(pr_damagemobj) & 1) + { + ang += ANG180; + thrust *= 4; + } + + ang >>= ANGLETOFINESHIFT; + target->momx += FixedMul (thrust, finecosine[ang]); + target->momy += FixedMul (thrust, finesine[ang]); + + /* killough 11/98: thrust objects hanging off ledges */ + if (target->intflags & MIF_FALLING && target->gear >= MAXGEAR) + target->gear = 0; + } + + // 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. + // killough 3/26/98: make god mode 100% god mode in non-compat mode + + if ((damage < 1000 || (!comp[comp_god] && (player->cheats&CF_GODMODE))) && + (player->cheats&CF_GODMODE || player->powers[pw_invulnerability])) + return; + + if (player->armortype) + { + int saved = player->armortype == 1 ? damage/3 : 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... + } + + if (source && target) + { + CheckGivenDamageTracer(source, damage); + } + + // do the damage + target->health -= damage; + if (target->health <= 0) + { + P_KillMobj (source, target); + return; + } + + // killough 9/7/98: keep track of targets so that friends can help friends + if (mbf_features) + { + /* If target is a player, set player's target to source, + * so that a friend can tell who's hurting a player + */ + if (player) + P_SetTarget(&target->target, source); + + /* killough 9/8/98: + * If target's health is less than 50%, move it to the front of its list. + * This will slightly increase the chances that enemies will choose to + * "finish it off", but its main purpose is to alert friends of danger. + */ + if (target->health*2 < target->info->spawnhealth) + { + thinker_t *cap = &thinkerclasscap[target->flags & MF_FRIEND ? + th_friends : th_enemies]; + (target->thinker.cprev->cnext = target->thinker.cnext)->cprev = + target->thinker.cprev; + (target->thinker.cnext = cap->cnext)->cprev = &target->thinker; + (target->thinker.cprev = cap)->cnext = &target->thinker; + } + } + + if (P_Random (pr_painchance) < target->info->painchance && + !(target->flags & MF_SKULLFLY)) { //killough 11/98: see below + if (mbf_features) + justhit = true; + else + target->flags |= MF_JUSTHIT; // fight back! + + //e6y + { + if(demo_compatibility) + if ((target->target == source || !target->target || + !(target->flags & target->target->flags & MF_FRIEND))) + target->flags |= MF_JUSTHIT; // fight back! + } + + P_SetMobjState(target, target->info->painstate); + }//e6y + + target->reactiontime = 0; // we're awake now... + + /* killough 9/9/98: cleaned up, made more consistent: */ + //e6y: Monsters could commit suicide in Doom v1.2 if they damaged themselves by exploding a barrel + if (source && (source != target || compatibility_level == doom_12_compatibility) && + source->type != MT_VILE && + (!target->threshold || target->type == MT_VILE) && + ((source->flags ^ target->flags) & MF_FRIEND || + monster_infighting || + !mbf_features)) + { + /* if not intent on another player, chase after this one + * + * killough 2/15/98: remember last enemy, to prevent + * sleeping early; 2/21/98: Place priority on players + * killough 9/9/98: cleaned up, made more consistent: + */ + + if (!target->lastenemy || target->lastenemy->health <= 0 || + (!mbf_features ? + !target->lastenemy->player : + !((target->flags ^ target->lastenemy->flags) & MF_FRIEND) && + target->target != source)) // remember last enemy - killough + P_SetTarget(&target->lastenemy, target->target); + + P_SetTarget(&target->target, source); // killough 11/98 + target->threshold = BASETHRESHOLD; + if (target->state == &states[target->info->spawnstate] + && target->info->seestate != S_NULL) + P_SetMobjState (target, target->info->seestate); + } + + /* killough 11/98: Don't attack a friend, unless hit by that friend. + * cph 2006/04/01 - implicitly this is only if mbf_features */ + if(!demo_compatibility) //e6y + if (justhit && (target->target == source || !target->target || + !(target->flags & target->target->flags & MF_FRIEND))) + target->flags |= MF_JUSTHIT; // fight back! +} diff --git a/src/p_inter.h b/src/p_inter.h new file mode 100644 index 0000000..39f5fc6 --- /dev/null +++ b/src/p_inter.h @@ -0,0 +1,76 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Thing events, and dehacked specified numbers controlling them. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_INTER__ +#define __P_INTER__ + +#include "d_player.h" +#include "p_mobj.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +/* Ty 03/09/98 Moved to an int in p_inter.c for deh and externalization */ +#define MAXHEALTH maxhealth + +/* follow a player exlusively for 3 seconds */ +#define BASETHRESHOLD (100) + +dboolean P_GivePower(player_t *, int); +void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher); +void P_DamageMobj(mobj_t *target,mobj_t *inflictor,mobj_t *source,int damage); + +/* killough 5/2/98: moved from d_deh.c, g_game.c, m_misc.c, others: */ + +extern int god_health; /* Ty 03/09/98 - deh support, see also p_inter.c */ +extern int idfa_armor; +extern int idfa_armor_class; +extern int idkfa_armor; +extern int idkfa_armor_class; /* Ty - end */ +/* Ty 03/13/98 - externalized initial settings for respawned player */ +extern int initial_health; +extern int initial_bullets; +extern int maxhealth; +extern int maxhealthbonus; +extern int max_armor; +extern int green_armor_class; +extern int blue_armor_class; +extern int max_soul; +extern int soul_health; +extern int mega_health; +extern int bfgcells; +extern int monsters_infight; // e6y: Dehacked support - monsters infight +extern int maxammo[], clipammo[]; + +#endif diff --git a/src/p_lights.c b/src/p_lights.c new file mode 100644 index 0000000..a4f3737 --- /dev/null +++ b/src/p_lights.c @@ -0,0 +1,443 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Action routines for lighting thinkers + * Spawn sector based lighting effects. + * Handle lighting linedef types + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" //jff 5/18/98 +#include "doomdef.h" +#include "m_random.h" +#include "r_main.h" +#include "p_spec.h" +#include "p_tick.h" + +////////////////////////////////////////////////////////// +// +// Lighting action routines, called once per tick +// +////////////////////////////////////////////////////////// + +// +// T_FireFlicker() +// +// Firelight flicker action routine, called once per tick +// +// Passed a fireflicker_t structure containing light levels and timing +// Returns nothing +// +void T_FireFlicker (fireflicker_t* flick) +{ + int amount; + + if (--flick->count) + return; + + amount = (P_Random(pr_lights)&3)*16; + + if (flick->sector->lightlevel - amount < flick->minlight) + flick->sector->lightlevel = flick->minlight; + else + flick->sector->lightlevel = flick->maxlight - amount; + + flick->count = 4; +} + +// +// T_LightFlash() +// +// Broken light flashing action routine, called once per tick +// +// Passed a lightflash_t structure containing light levels and timing +// Returns nothing +// +void T_LightFlash (lightflash_t* flash) +{ + if (--flash->count) + return; + + if (flash->sector->lightlevel == flash->maxlight) + { + flash-> sector->lightlevel = flash->minlight; + flash->count = (P_Random(pr_lights)&flash->mintime)+1; + } + else + { + flash-> sector->lightlevel = flash->maxlight; + flash->count = (P_Random(pr_lights)&flash->maxtime)+1; + } + +} + +// +// T_StrobeFlash() +// +// Strobe light flashing action routine, called once per tick +// +// Passed a strobe_t structure containing light levels and timing +// Returns nothing +// +void T_StrobeFlash (strobe_t* flash) +{ + if (--flash->count) + return; + + if (flash->sector->lightlevel == flash->minlight) + { + flash-> sector->lightlevel = flash->maxlight; + flash->count = flash->brighttime; + } + else + { + flash-> sector->lightlevel = flash->minlight; + flash->count =flash->darktime; + } +} + +// +// T_Glow() +// +// Glowing light action routine, called once per tick +// +// Passed a glow_t structure containing light levels and timing +// Returns nothing +// + +void T_Glow(glow_t* g) +{ + switch(g->direction) + { + case -1: + // light dims + g->sector->lightlevel -= GLOWSPEED; + if (g->sector->lightlevel <= g->minlight) + { + g->sector->lightlevel += GLOWSPEED; + g->direction = 1; + } + break; + + case 1: + // light brightens + g->sector->lightlevel += GLOWSPEED; + if (g->sector->lightlevel >= g->maxlight) + { + g->sector->lightlevel -= GLOWSPEED; + g->direction = -1; + } + break; + } +} + +////////////////////////////////////////////////////////// +// +// Sector lighting type spawners +// +// After the map has been loaded, each sector is scanned +// for specials that spawn thinkers +// +////////////////////////////////////////////////////////// + +// +// P_SpawnFireFlicker() +// +// Spawns a fire flicker lighting thinker +// +// Passed the sector that spawned the thinker +// Returns nothing +// +void P_SpawnFireFlicker (sector_t* sector) +{ + fireflicker_t* flick; + + // Note that we are resetting sector attributes. + // Nothing special about it during gameplay. + sector->special &= ~31; //jff 3/14/98 clear non-generalized sector type + + flick = Z_Malloc ( sizeof(*flick), PU_LEVSPEC, 0); + + memset(flick, 0, sizeof(*flick)); + P_AddThinker (&flick->thinker); + + flick->thinker.function = T_FireFlicker; + flick->sector = sector; + flick->maxlight = sector->lightlevel; + flick->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel)+16; + flick->count = 4; +} + +// +// P_SpawnLightFlash() +// +// Spawns a broken light flash lighting thinker +// +// Passed the sector that spawned the thinker +// Returns nothing +// +void P_SpawnLightFlash (sector_t* sector) +{ + lightflash_t* flash; + + // nothing special about it during gameplay + sector->special &= ~31; //jff 3/14/98 clear non-generalized sector type + + flash = Z_Malloc ( sizeof(*flash), PU_LEVSPEC, 0); + + memset(flash, 0, sizeof(*flash)); + P_AddThinker (&flash->thinker); + + flash->thinker.function = T_LightFlash; + flash->sector = sector; + flash->maxlight = sector->lightlevel; + + flash->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel); + flash->maxtime = 64; + flash->mintime = 7; + flash->count = (P_Random(pr_lights)&flash->maxtime)+1; +} + +// +// P_SpawnStrobeFlash +// +// Spawns a blinking light thinker +// +// Passed the sector that spawned the thinker, speed of blinking +// and whether blinking is to by syncrhonous with other sectors +// +// Returns nothing +// +void P_SpawnStrobeFlash +( sector_t* sector, + int fastOrSlow, + int inSync ) +{ + strobe_t* flash; + + flash = Z_Malloc ( sizeof(*flash), PU_LEVSPEC, 0); + + memset(flash, 0, sizeof(*flash)); + P_AddThinker (&flash->thinker); + + flash->sector = sector; + flash->darktime = fastOrSlow; + flash->brighttime = STROBEBRIGHT; + flash->thinker.function = T_StrobeFlash; + flash->maxlight = sector->lightlevel; + flash->minlight = P_FindMinSurroundingLight(sector, sector->lightlevel); + + if (flash->minlight == flash->maxlight) + flash->minlight = 0; + + // nothing special about it during gameplay + sector->special &= ~31; //jff 3/14/98 clear non-generalized sector type + + if (!inSync) + flash->count = (P_Random(pr_lights)&7)+1; + else + flash->count = 1; +} + +// +// P_SpawnGlowingLight() +// +// Spawns a glowing light (smooth oscillation from min to max) thinker +// +// Passed the sector that spawned the thinker +// Returns nothing +// +void P_SpawnGlowingLight(sector_t* sector) +{ + glow_t* g; + + g = Z_Malloc( sizeof(*g), PU_LEVSPEC, 0); + + memset(g, 0, sizeof(*g)); + P_AddThinker(&g->thinker); + + g->sector = sector; + g->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel); + g->maxlight = sector->lightlevel; + g->thinker.function = T_Glow; + g->direction = -1; + + sector->special &= ~31; //jff 3/14/98 clear non-generalized sector type +} + +////////////////////////////////////////////////////////// +// +// Linedef lighting function handlers +// +////////////////////////////////////////////////////////// + +// +// EV_StartLightStrobing() +// +// Start strobing lights (usually from a trigger) +// +// Passed the line that activated the strobing +// Returns true +// +// jff 2/12/98 added int return value, fixed return +// +int EV_StartLightStrobing(line_t* line) +{ + int secnum; + sector_t* sec; + + secnum = -1; + // start lights strobing in all sectors tagged same as line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + // if already doing a lighting function, don't start a second + if (P_SectorActive(lighting_special,sec)) //jff 2/22/98 + continue; + + P_SpawnStrobeFlash (sec,SLOWDARK, 0); + } + return 1; +} + +// +// EV_TurnTagLightsOff() +// +// Turn line's tagged sector's lights to min adjacent neighbor level +// +// Passed the line that activated the lights being turned off +// Returns true +// +// jff 2/12/98 added int return value, fixed return +// +int EV_TurnTagLightsOff(line_t* line) +{ + int j; + + // search sectors for those with same tag as activating line + + // killough 10/98: replaced inefficient search with fast search + for (j = -1; (j = P_FindSectorFromLineTag(line,j)) >= 0;) + { + sector_t *sector = sectors + j, *tsec; + int i, min = sector->lightlevel; + // find min neighbor light level + for (i = 0;i < sector->linecount; i++) + if ((tsec = getNextSector(sector->lines[i], sector)) && + tsec->lightlevel < min) + min = tsec->lightlevel; + sector->lightlevel = min; + } + return 1; +} + +// +// EV_LightTurnOn() +// +// Turn sectors tagged to line lights on to specified or max neighbor level +// +// Passed the activating line, and a level to set the light to +// If level passed is 0, the maximum neighbor lighting is used +// Returns true +// +// jff 2/12/98 added int return value, fixed return +// +int EV_LightTurnOn(line_t *line, int bright) +{ + int i; + + // search all sectors for ones with same tag as activating line + + // killough 10/98: replace inefficient search with fast search + for (i = -1; (i = P_FindSectorFromLineTag(line,i)) >= 0;) + { + sector_t *temp, *sector = sectors+i; + int j, tbright = bright; //jff 5/17/98 search for maximum PER sector + + // bright = 0 means to search for highest light level surrounding sector + + if (!bright) + for (j = 0;j < sector->linecount; j++) + if ((temp = getNextSector(sector->lines[j],sector)) && + temp->lightlevel > tbright) + tbright = temp->lightlevel; + + sector->lightlevel = tbright; + + //jff 5/17/98 unless compatibility optioned + //then maximum near ANY tagged sector + if (comp[comp_model]) + bright = tbright; + } + return 1; +} + +/* killough 10/98: + * + * EV_LightTurnOnPartway() + * + * Turn sectors tagged to line lights on to specified or max neighbor level + * + * Passed the activating line, and a light level fraction between 0 and 1. + * Sets the light to min on 0, max on 1, and interpolates in-between. + * Used for doors with gradual lighting effects. + * + * Returns true + */ + +int EV_LightTurnOnPartway(line_t *line, fixed_t level) +{ + int i; + + if (level < 0) // clip at extremes + level = 0; + if (level > FRACUNIT) + level = FRACUNIT; + + // search all sectors for ones with same tag as activating line + for (i = -1; (i = P_FindSectorFromLineTag(line,i)) >= 0;) + { + sector_t *temp, *sector = sectors+i; + int j, bright = 0, min = sector->lightlevel; + + for (j = 0; j < sector->linecount; j++) + if ((temp = getNextSector(sector->lines[j],sector))) + { + if (temp->lightlevel > bright) + bright = temp->lightlevel; + if (temp->lightlevel < min) + min = temp->lightlevel; + } + + sector->lightlevel = // Set level in-between extremes + (level * bright + (FRACUNIT-level) * min) >> FRACBITS; + } + return 1; +} + diff --git a/src/p_map.c b/src/p_map.c new file mode 100644 index 0000000..c5a9991 --- /dev/null +++ b/src/p_map.c @@ -0,0 +1,2425 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Movement, collision handling. + * Shooting and aiming. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "r_main.h" +#include "p_mobj.h" +#include "p_maputl.h" +#include "p_map.h" +#include "p_setup.h" +#include "p_spec.h" +#include "s_sound.h" +#include "sounds.h" +#include "p_inter.h" +#include "m_random.h" +#include "m_bbox.h" +#include "lprintf.h" +#include "m_argv.h" +#include "g_game.h" +#include "g_overflow.h" +#include "hu_tracers.h" +#include "e6y.h"//e6y + +static mobj_t *tmthing; +static fixed_t tmx; +static fixed_t tmy; +static int pe_x; // Pain Elemental position for Lost Soul checks // phares +static int pe_y; // Pain Elemental position for Lost Soul checks // phares +static int ls_x; // Lost Soul position for Lost Soul checks // phares +static int ls_y; // Lost Soul position for Lost Soul checks // phares + +// +// SECTOR HEIGHT CHANGING +// After modifying a sectors floor or ceiling height, +// call this routine to adjust the positions +// of all things that touch the sector. +// +// If anything doesn't fit anymore, true will be returned. +// If crunch is true, they will take damage +// as they are being crushed. +// If Crunch is false, you should set the sector height back +// the way it was and call P_ChangeSector again +// to undo the changes. +// + +static dboolean crushchange, nofit; + +// If "floatok" true, move would be ok +// if within "tmfloorz - tmceilingz". + +dboolean floatok; + +/* killough 11/98: if "felldown" true, object was pushed down ledge */ +dboolean felldown; + +// The tm* items are used to hold information globally, usually for +// line or object intersection checking + +fixed_t tmbbox[4]; // bounding box for line intersection checks +fixed_t tmfloorz; // floor you'd hit if free to fall +fixed_t tmceilingz; // ceiling of sector you're in +fixed_t tmdropoffz; // dropoff on other side of line you're crossing + +// keep track of the line that lowers the ceiling, +// so missiles don't explode against sky hack walls + +line_t *ceilingline; +line_t *blockline; /* killough 8/11/98: blocking linedef */ +line_t *floorline; /* killough 8/1/98: Highest touched floor */ +static int tmunstuck; /* killough 8/1/98: whether to allow unsticking */ + +// keep track of special lines as they are hit, +// but don't process them until the move is proven valid + +// 1/11/98 killough: removed limit on special lines crossed +line_t **spechit; // new code -- killough +static int spechit_max; // killough + +int numspechit; + +// Temporary holder for thing_sectorlist threads +msecnode_t* sector_list = NULL; // phares 3/16/98 + +// +// TELEPORT MOVE +// + +// +// PIT_StompThing +// + +static dboolean telefrag; /* killough 8/9/98: whether to telefrag at exit */ + +dboolean PIT_StompThing (mobj_t* thing) +{ + fixed_t blockdist; + + // phares 9/10/98: moved this self-check to start of routine + + // don't clip against self + + if (thing == tmthing) + return true; + + if (!(thing->flags & MF_SHOOTABLE)) // Can't shoot it? Can't stomp it! + return true; + + blockdist = thing->radius + tmthing->radius; + + if (D_abs(thing->x - tmx) >= blockdist || D_abs(thing->y - tmy) >= blockdist) + return true; // didn't hit it + + // monsters don't stomp things except on boss level + if (!telefrag) // killough 8/9/98: make consistent across all levels + return false; + + P_DamageMobj (thing, tmthing, tmthing, 10000); // Stomp! + + return true; +} + + +/* + * killough 8/28/98: + * + * P_GetFriction() + * + * Returns the friction associated with a particular mobj. + */ + +int P_GetFriction(const mobj_t *mo, int *frictionfactor) +{ + int friction = ORIG_FRICTION; + int movefactor = ORIG_FRICTION_FACTOR; + const msecnode_t *m; + const sector_t *sec; + + /* Assign the friction value to objects on the floor, non-floating, + * and clipped. Normally the object's friction value is kept at + * ORIG_FRICTION and this thinker changes it for icy or muddy floors. + * + * When the object is straddling sectors with the same + * floorheight that have different frictions, use the lowest + * friction value (muddy has precedence over icy). + */ + + if (mo->flags & MF_FLY) + { + friction = FRICTION_FLY; + } + else + { + if (!(mo->flags & (MF_NOCLIP|MF_NOGRAVITY)) + && (mbf_features || (mo->player && !compatibility)) && + variable_friction) + for (m = mo->touching_sectorlist; m; m = m->m_tnext) + if ((sec = m->m_sector)->special & FRICTION_MASK && + (sec->friction < friction || friction == ORIG_FRICTION) && + (mo->z <= sec->floorheight || + (sec->heightsec != -1 && + mo->z <= sectors[sec->heightsec].floorheight && + mbf_features))) + friction = sec->friction, movefactor = sec->movefactor; + } + + if (frictionfactor) + *frictionfactor = movefactor; + + return friction; +} + +/* phares 3/19/98 + * P_GetMoveFactor() returns the value by which the x,y + * movements are multiplied to add to player movement. + * + * killough 8/28/98: rewritten + */ + +int P_GetMoveFactor(mobj_t *mo, int *frictionp) +{ + int movefactor, friction; + + //e6y + if (!mbf_features && !prboom_comp[PC_PRBOOM_FRICTION].state) + { + int momentum; + + movefactor = ORIG_FRICTION_FACTOR; + + if (!compatibility && variable_friction && + !(mo->flags & (MF_NOGRAVITY | MF_NOCLIP))) + { + friction = mo->friction; + if (friction == ORIG_FRICTION) // normal floor + ; + else if (friction > ORIG_FRICTION) // ice + { + movefactor = mo->movefactor; + ((mobj_t*)mo)->movefactor = ORIG_FRICTION_FACTOR; // reset + } + else // sludge + { + + // phares 3/11/98: you start off slowly, then increase as + // you get better footing + + momentum = (P_AproxDistance(mo->momx,mo->momy)); + movefactor = mo->movefactor; + if (momentum > MORE_FRICTION_MOMENTUM<<2) + movefactor <<= 3; + + else if (momentum > MORE_FRICTION_MOMENTUM<<1) + movefactor <<= 2; + + else if (momentum > MORE_FRICTION_MOMENTUM) + movefactor <<= 1; + + ((mobj_t*)mo)->movefactor = ORIG_FRICTION_FACTOR; // reset + } + } // ^ + + return(movefactor); // | + } + + // If the floor is icy or muddy, it's harder to get moving. This is where + // the different friction factors are applied to 'trying to move'. In + // p_mobj.c, the friction factors are applied as you coast and slow down. + + if ((friction = P_GetFriction(mo, &movefactor)) < ORIG_FRICTION) + { + // phares 3/11/98: you start off slowly, then increase as + // you get better footing + + int momentum = P_AproxDistance(mo->momx,mo->momy); + + if (momentum > MORE_FRICTION_MOMENTUM<<2) + movefactor <<= 3; + else if (momentum > MORE_FRICTION_MOMENTUM<<1) + movefactor <<= 2; + else if (momentum > MORE_FRICTION_MOMENTUM) + movefactor <<= 1; + } + + if (frictionp) + *frictionp = friction; + + return movefactor; +} + +// +// P_TeleportMove +// + +dboolean P_TeleportMove (mobj_t* thing,fixed_t x,fixed_t y, dboolean boss) +{ + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + + subsector_t* newsubsec; + + /* killough 8/9/98: make telefragging more consistent, preserve compatibility */ + telefrag = thing->player || + (!comp[comp_telefrag] ? boss : (gamemap==30)); + + // kill anything occupying the position + + tmthing = thing; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + newsubsec = R_PointInSubsector (x,y); + ceilingline = 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 = P_GetSafeBlockX(tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS); + xh = P_GetSafeBlockX(tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS); + yl = P_GetSafeBlockY(tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS); + yh = P_GetSafeBlockY(tmbbox[BOXTOP] - bmaporgy + MAXRADIUS); + + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + if (!P_BlockThingsIterator(bx,by,PIT_StompThing)) + return false; + + // the move is ok, + // so unlink from the old position & link into the new position + + P_UnsetThingPosition (thing); + + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->dropoffz = tmdropoffz; // killough 11/98 + + thing->x = x; + thing->y = y; + + P_SetThingPosition (thing); + + thing->PrevX = x; + thing->PrevY = y; + thing->PrevZ = thing->floorz; + + return true; +} + + +// +// MOVEMENT ITERATOR FUNCTIONS +// + +// // phares +// PIT_CrossLine // | +// Checks to see if a PE->LS trajectory line crosses a blocking // V +// line. Returns false if it does. +// +// tmbbox holds the bounding box of the trajectory. If that box +// does not touch the bounding box of the line in question, +// then the trajectory is not blocked. If the PE is on one side +// of the line and the LS is on the other side, then the +// trajectory is blocked. +// +// Currently this assumes an infinite line, which is not quite +// correct. A more correct solution would be to check for an +// intersection of the trajectory and the line, but that takes +// longer and probably really isn't worth the effort. +// + +static // killough 3/26/98: make static +dboolean PIT_CrossLine (line_t* ld) +{ + if (!(ld->flags & ML_TWOSIDED) || + (ld->flags & (ML_BLOCKING|ML_BLOCKMONSTERS))) + if (!(tmbbox[BOXLEFT] > ld->bbox[BOXRIGHT] || + tmbbox[BOXRIGHT] < ld->bbox[BOXLEFT] || + tmbbox[BOXTOP] < ld->bbox[BOXBOTTOM] || + tmbbox[BOXBOTTOM] > ld->bbox[BOXTOP])) + if (P_PointOnLineSide(pe_x,pe_y,ld) != P_PointOnLineSide(ls_x,ls_y,ld)) + return(false); // line blocks trajectory // ^ + return(true); // line doesn't block trajectory // | +} // phares + + +/* killough 8/1/98: used to test intersection between thing and line + * assuming NO movement occurs -- used to avoid sticky situations. + */ + +static int untouched(line_t *ld) +{ + fixed_t x, y, tmbbox[4]; + return + (tmbbox[BOXRIGHT] = (x=tmthing->x)+tmthing->radius) <= ld->bbox[BOXLEFT] || + (tmbbox[BOXLEFT] = x-tmthing->radius) >= ld->bbox[BOXRIGHT] || + (tmbbox[BOXTOP] = (y=tmthing->y)+tmthing->radius) <= ld->bbox[BOXBOTTOM] || + (tmbbox[BOXBOTTOM] = y-tmthing->radius) >= ld->bbox[BOXTOP] || + P_BoxOnLineSide(tmbbox, ld) != -1; +} + +// +// PIT_CheckLine +// Adjusts tmfloorz and tmceilingz as lines are contacted +// + +static // killough 3/26/98: make static +dboolean PIT_CheckLine (line_t* ld) +{ + if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] + || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] + || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] + || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] ) + return true; // didn't hit it + + if (P_BoxOnLineSide(tmbbox, ld) != -1) + return true; // didn't hit it + + // 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. + + // killough 7/24/98: allow player to move out of 1s wall, to prevent sticking + if (!ld->backsector) // one sided line + { + blockline = ld; + return tmunstuck && !untouched(ld) && + FixedMul(tmx-tmthing->x,ld->dy) > FixedMul(tmy-tmthing->y,ld->dx); + } + + // killough 8/10/98: allow bouncing objects to pass through as missiles + if (!(tmthing->flags & (MF_MISSILE | MF_BOUNCES))) + { + if (ld->flags & ML_BLOCKING) // explicitly blocking everything + return tmunstuck && !untouched(ld); // killough 8/1/98: allow escape + + // killough 8/9/98: monster-blockers don't affect friends + if (!(tmthing->flags & MF_FRIEND || tmthing->player) + && ld->flags & ML_BLOCKMONSTERS) + return false; // block monsters only + } + + // set openrange, opentop, openbottom + // these define a 'window' from one sector to another across this line + + P_LineOpening (ld); + + // adjust floor & ceiling heights + + if (opentop < tmceilingz) + { + tmceilingz = opentop; + ceilingline = ld; + blockline = ld; + } + + if (openbottom > tmfloorz) + { + tmfloorz = openbottom; + floorline = ld; // killough 8/1/98: remember floor linedef + blockline = ld; + } + + if (lowfloor < tmdropoffz) + tmdropoffz = lowfloor; + + // if contacted a special line, add it to the list + + CheckLinesCrossTracer(ld);//e6y + if (ld->special) + { + // 1/11/98 killough: remove limit on lines hit, by array doubling + if (numspechit >= spechit_max) { + spechit_max = spechit_max ? spechit_max*2 : 8; + spechit = realloc(spechit,sizeof *spechit*spechit_max); // killough + } + spechit[numspechit++] = ld; + // e6y: Spechits overrun emulation code + if (numspechit > 8 && demo_compatibility) + { + static spechit_overrun_param_t spechit_overrun_param = { + NULL, // line_t *line; + + &spechit, // line_t **spechit; + &numspechit, // int *numspechit; + + tmbbox, // fixed_t *tmbbox[4]; + &tmfloorz, // fixed_t *tmfloorz; + &tmceilingz, // fixed_t *tmceilingz; + + &crushchange, // dboolean *crushchange; + &nofit, // dboolean *nofit; + }; + spechit_overrun_param.line = ld; + SpechitOverrun(&spechit_overrun_param); + } + } + + return true; +} + +// +// PIT_CheckThing +// + +static dboolean PIT_CheckThing(mobj_t *thing) // killough 3/26/98: make static +{ + fixed_t blockdist; + int damage; + + // killough 11/98: add touchy things + if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE|MF_TOUCHY))) + return true; + + blockdist = thing->radius + tmthing->radius; + + if (D_abs(thing->x - tmx) >= blockdist || D_abs(thing->y - tmy) >= blockdist) + return true; // didn't hit it + + // killough 11/98: + // + // This test has less information content (it's almost always false), so it + // should not be moved up to first, as it adds more overhead than it removes. + + // don't clip against self + + if (thing == tmthing) + return true; + + /* killough 11/98: + * + * TOUCHY flag, for mines or other objects which die on contact with solids. + * If a solid object of a different type comes in contact with a touchy + * thing, and the touchy thing is not the sole one moving relative to fixed + * surroundings such as walls, then the touchy thing dies immediately. + */ + + if (thing->flags & MF_TOUCHY && // touchy object + tmthing->flags & MF_SOLID && // solid object touches it + thing->health > 0 && // touchy object is alive + (thing->intflags & MIF_ARMED || // Thing is an armed mine + sentient(thing)) && // ... or a sentient thing + (thing->type != tmthing->type || // only different species + thing->type == MT_PLAYER) && // ... or different players + thing->z + thing->height >= tmthing->z && // touches vertically + tmthing->z + tmthing->height >= thing->z && + (thing->type ^ MT_PAIN) | // PEs and lost souls + (tmthing->type ^ MT_SKULL) && // are considered same + (thing->type ^ MT_SKULL) | // (but Barons & Knights + (tmthing->type ^ MT_PAIN)) // are intentionally not) + { + P_DamageMobj(thing, NULL, NULL, thing->health); // kill object + return true; + } + + // check for skulls slamming into things + + if (tmthing->flags & MF_SKULLFLY) + { + // A flying skull is smacking something. + // Determine damage amount, and the skull comes to a dead stop. + + int damage = ((P_Random(pr_skullfly)%8)+1)*tmthing->info->damage; + + P_DamageMobj (thing, tmthing, tmthing, damage); + + tmthing->flags &= ~MF_SKULLFLY; + tmthing->momx = tmthing->momy = tmthing->momz = 0; + + P_SetMobjState (tmthing, tmthing->info->spawnstate); + + return false; // stop moving + } + + // missiles can hit other things + // killough 8/10/98: bouncing non-solid things can hit other things too + + if (tmthing->flags & MF_MISSILE || (tmthing->flags & MF_BOUNCES && + !(tmthing->flags & MF_SOLID))) + { + // 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))) + { + if (thing == tmthing->target) + return true; // Don't hit same species as originator. + else + // e6y: Dehacked support - monsters infight + if (thing->type != MT_PLAYER && !monsters_infight) // Explode, but do no damage. + return false; // Let players missile other players. + } + + // killough 8/10/98: if moving thing is not a missile, no damage + // is inflicted, and momentum is reduced if object hit is solid. + + if (!(tmthing->flags & MF_MISSILE)) { + if (!(thing->flags & MF_SOLID)) { + return true; + } else { + tmthing->momx = -tmthing->momx; + tmthing->momy = -tmthing->momy; + if (!(tmthing->flags & MF_NOGRAVITY)) + { + tmthing->momx >>= 2; + tmthing->momy >>= 2; + } + return false; + } + } + + if (!(thing->flags & MF_SHOOTABLE)) + return !(thing->flags & MF_SOLID); // didn't do any damage + + // damage / explode + + damage = ((P_Random(pr_damage)%8)+1)*tmthing->info->damage; + P_DamageMobj (thing, tmthing, tmthing->target, damage); + + // don't traverse any more + return false; + } + + // check for special pickup + + if (thing->flags & MF_SPECIAL) + { + uint_64_t solid = thing->flags & MF_SOLID; + if (tmthing->flags & MF_PICKUP) + P_TouchSpecialThing(thing, tmthing); // can remove thing + return !solid; + } + + // RjY + // comperr_hangsolid, an attempt to handle blocking hanging bodies + // A solid hanging body will allow sufficiently small things underneath it. + if (comperr(comperr_hangsolid) && + !((~thing->flags) & (MF_SOLID | MF_SPAWNCEILING)) // solid and hanging + // invert everything, then both bits should be clear + && tmthing->z + tmthing->height <= thing->z) // head height <= base + // top of thing trying to move under the body <= bottom of body + { + tmceilingz = thing->z; // pretend ceiling height is at body's base + return true; + } + + // killough 3/16/98: Allow non-solid moving objects to move through solid + // ones, by allowing the moving thing (tmthing) to move if it's non-solid, + // despite another solid thing being in the way. + // killough 4/11/98: Treat no-clipping things as not blocking + // ...but not in demo_compatibility mode + + // e6y + // Correction of wrong return value with demo_compatibility. + // There is no more synch on http://www.doomworld.com/sda/dwdemo/w303-115.zip + // (with correction in setMobjInfoValue) + if (demo_compatibility && !prboom_comp[PC_TREAT_NO_CLIPPING_THINGS_AS_NOT_BLOCKING].state) + return !(thing->flags & MF_SOLID); + else + + return !((thing->flags & MF_SOLID && !(thing->flags & MF_NOCLIP)) + && (tmthing->flags & MF_SOLID || demo_compatibility)); + + // return !(thing->flags & MF_SOLID); // old code -- killough +} + +// This routine checks for Lost Souls trying to be spawned // phares +// across 1-sided lines, impassible lines, or "monsters can't // | +// cross" lines. Draw an imaginary line between the PE // V +// and the new Lost Soul spawn spot. If that line crosses +// a 'blocking' line, then disallow the spawn. Only search +// lines in the blocks of the blockmap where the bounding box +// of the trajectory line resides. Then check bounding box +// of the trajectory vs. the bounding box of each blocking +// line to see if the trajectory and the blocking line cross. +// Then check the PE and LS to see if they're on different +// sides of the blocking line. If so, return true, otherwise +// false. + +dboolean Check_Sides(mobj_t* actor, int x, int y) +{ + int bx,by,xl,xh,yl,yh; + + pe_x = actor->x; + pe_y = actor->y; + ls_x = x; + ls_y = y; + + // Here is the bounding box of the trajectory + + tmbbox[BOXLEFT] = pe_x < x ? pe_x : x; + tmbbox[BOXRIGHT] = pe_x > x ? pe_x : x; + tmbbox[BOXTOP] = pe_y > y ? pe_y : y; + tmbbox[BOXBOTTOM] = pe_y < y ? pe_y : y; + + // Determine which blocks to look in for blocking lines + + xl = P_GetSafeBlockX(tmbbox[BOXLEFT] - bmaporgx); + xh = P_GetSafeBlockX(tmbbox[BOXRIGHT] - bmaporgx); + yl = P_GetSafeBlockY(tmbbox[BOXBOTTOM] - bmaporgy); + yh = P_GetSafeBlockY(tmbbox[BOXTOP] - bmaporgy); + + // xl->xh, yl->yh determine the mapblock set to search + + validcount++; // prevents checking same line twice + for (bx = xl ; bx <= xh ; bx++) + for (by = yl ; by <= yh ; by++) + if (!P_BlockLinesIterator(bx,by,PIT_CrossLine)) + return true; // ^ + return(false); // | +} // phares + +// +// MOVEMENT CLIPPING +// + +// +// P_CheckPosition +// This is purely informative, nothing is modified +// (except things picked up). +// +// in: +// a mobj_t (can be valid or invalid) +// a position to be checked +// (doesn't need to be related to the mobj_t->x,y) +// +// during: +// special things are touched if MF_PICKUP +// early out on solid lines? +// +// out: +// newsubsec +// floorz +// ceilingz +// tmdropoffz +// the lowest point contacted +// (monsters won't move to a dropoff) +// speciallines[] +// numspeciallines +// + +dboolean P_CheckPosition (mobj_t* thing,fixed_t x,fixed_t y) +{ + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + subsector_t* newsubsec; + + tmthing = thing; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + newsubsec = R_PointInSubsector (x,y); + floorline = blockline = ceilingline = NULL; // killough 8/1/98 + + // Whether object can get out of a sticky situation: + tmunstuck = thing->player && /* only players */ + thing->player->mo == thing && /* not voodoo dolls */ + mbf_features; /* not under old demos */ + + // 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 ( tmthing->flags & 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 = P_GetSafeBlockX(tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS); + xh = P_GetSafeBlockX(tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS); + yl = P_GetSafeBlockY(tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS); + yh = P_GetSafeBlockY(tmbbox[BOXTOP] - bmaporgy + MAXRADIUS); + + + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + if (!P_BlockThingsIterator(bx,by,PIT_CheckThing)) + return false; + + // check lines + + xl = P_GetSafeBlockX(tmbbox[BOXLEFT] - bmaporgx); + xh = P_GetSafeBlockX(tmbbox[BOXRIGHT] - bmaporgx); + yl = P_GetSafeBlockY(tmbbox[BOXBOTTOM] - bmaporgy); + yh = P_GetSafeBlockY(tmbbox[BOXTOP] - bmaporgy); + + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + if (!P_BlockLinesIterator (bx,by,PIT_CheckLine)) + return false; // doesn't fit + + ClearLinesCrossTracer();//e6y + return true; +} + + +// +// P_TryMove +// Attempt to move to a new position, +// crossing special lines unless MF_TELEPORT is set. +// +dboolean P_TryMove(mobj_t* thing,fixed_t x,fixed_t y, + dboolean dropoff) // killough 3/15/98: allow dropoff as option + { + fixed_t oldx; + fixed_t oldy; + + felldown = floatok = false; // killough 11/98 + + if (!P_CheckPosition (thing, x, y)) + return false; // solid wall or thing + + if ( !(thing->flags & MF_NOCLIP) ) + { + if (thing->flags & MF_FLY) + { + // When flying, slide up or down blocking lines until the actor + // is not blocked. + if (thing->z+thing->height > tmceilingz) + { + thing->momz = -8*FRACUNIT; + return false; + } + else if (thing->z < tmfloorz && tmfloorz-tmdropoffz > 24*FRACUNIT) + { + thing->momz = 8*FRACUNIT; + return false; + } + } + // killough 7/26/98: reformatted slightly + // killough 8/1/98: Possibly allow escape if otherwise stuck + + if (tmceilingz - tmfloorz < thing->height || // doesn't fit + // mobj must lower to fit + (floatok = true, !(thing->flags & MF_TELEPORT) && + tmceilingz - thing->z < thing->height && !(thing->flags & MF_FLY)) || + // too big a step up + (!(thing->flags & MF_TELEPORT) && + tmfloorz - thing->z > 24*FRACUNIT)) + return tmunstuck + && !(ceilingline && untouched(ceilingline)) + && !( floorline && untouched( floorline)); + + /* killough 3/15/98: Allow certain objects to drop off + * killough 7/24/98, 8/1/98: + * Prevent monsters from getting stuck hanging off ledges + * killough 10/98: Allow dropoffs in controlled circumstances + * killough 11/98: Improve symmetry of clipping on stairs + */ + if (!(thing->flags & (MF_DROPOFF|MF_FLOAT))) { + if (comp[comp_dropoff]) + { + // e6y + // Fix demosync bug in mbf compatibility mode + // There is no more desync on v2-2822.lmp/vrack2.wad + // -force_no_dropoff command-line switch is for mbf_compatibility demos + // recorded with prboom 2.2.2 - 2.4.7 + // Links: + // http://competn.doom2.net/pub/sda/t-z/v2-2822.zip + // http://www.doomworld.com/idgames/index.php?id=11138 + if ((compatibility || !dropoff + || (!prboom_comp[PC_NO_DROPOFF].state && mbf_features && compatibility_level <= prboom_2_compatibility)) + && (tmfloorz - tmdropoffz > 24*FRACUNIT)) + return false; // don't stand over a dropoff + } + else + if (!dropoff || (dropoff==2 && // large jump down (e.g. dogs) + (tmfloorz-tmdropoffz > 128*FRACUNIT || + !thing->target || thing->target->z >tmdropoffz))) + { + if (!monkeys || !mbf_features ? + tmfloorz - tmdropoffz > 24*FRACUNIT : + thing->floorz - tmfloorz > 24*FRACUNIT || + thing->dropoffz - tmdropoffz > 24*FRACUNIT) + return false; + } + else { /* dropoff allowed -- check for whether it fell more than 24 */ + felldown = !(thing->flags & MF_NOGRAVITY) && + thing->z - tmfloorz > 24*FRACUNIT; + } + } + + if (thing->flags & MF_BOUNCES && // killough 8/13/98 + !(thing->flags & (MF_MISSILE|MF_NOGRAVITY)) && + !sentient(thing) && tmfloorz - thing->z > 16*FRACUNIT) + return false; // too big a step up for bouncers under gravity + + // killough 11/98: prevent falling objects from going up too many steps + if (thing->intflags & MIF_FALLING && tmfloorz - thing->z > + FixedMul(thing->momx,thing->momx)+FixedMul(thing->momy,thing->momy)) + return false; + } + + // the move is ok, + // so unlink from the old position and link into the new position + + P_UnsetThingPosition (thing); + + oldx = thing->x; + oldy = thing->y; + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->dropoffz = tmdropoffz; // killough 11/98: keep track of dropoffs + thing->x = x; + thing->y = y; + + P_SetThingPosition (thing); + + // if any special lines were hit, do the effect + + if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) ) + while (numspechit--) + if (spechit[numspechit]->special) // see if the line was crossed + { + int oldside; + if ((oldside = P_PointOnLineSide(oldx, oldy, spechit[numspechit])) != + P_PointOnLineSide(thing->x, thing->y, spechit[numspechit])) + P_CrossSpecialLine(spechit[numspechit], oldside, thing, false); + } + + return true; + } + +/* + * killough 9/12/98: + * + * Apply "torque" to objects hanging off of ledges, so that they + * fall off. It's not really torque, since Doom has no concept of + * rotation, but it's a convincing effect which avoids anomalies + * such as lifeless objects hanging more than halfway off of ledges, + * and allows objects to roll off of the edges of moving lifts, or + * to slide up and then back down stairs, or to fall into a ditch. + * If more than one linedef is contacted, the effects are cumulative, + * so balancing is possible. + */ + +static dboolean PIT_ApplyTorque(line_t *ld) +{ + if (ld->backsector && // If thing touches two-sided pivot linedef + tmbbox[BOXRIGHT] > ld->bbox[BOXLEFT] && + tmbbox[BOXLEFT] < ld->bbox[BOXRIGHT] && + tmbbox[BOXTOP] > ld->bbox[BOXBOTTOM] && + tmbbox[BOXBOTTOM] < ld->bbox[BOXTOP] && + P_BoxOnLineSide(tmbbox, ld) == -1) + { + mobj_t *mo = tmthing; + + fixed_t dist = // lever arm + + (ld->dx >> FRACBITS) * (mo->y >> FRACBITS) + - (ld->dy >> FRACBITS) * (mo->x >> FRACBITS) + - (ld->dx >> FRACBITS) * (ld->v1->y >> FRACBITS) + + (ld->dy >> FRACBITS) * (ld->v1->x >> FRACBITS); + + if (dist < 0 ? // dropoff direction + ld->frontsector->floorheight < mo->z && + ld->backsector->floorheight >= mo->z : + ld->backsector->floorheight < mo->z && + ld->frontsector->floorheight >= mo->z) + { + /* At this point, we know that the object straddles a two-sided + * linedef, and that the object's center of mass is above-ground. + */ + + fixed_t x = D_abs(ld->dx), y = D_abs(ld->dy); + + if (y > x) + { + fixed_t t = x; + x = y; + y = t; + } + + y = finesine[(tantoangle[FixedDiv(y,x)>>DBITS] + + ANG90) >> ANGLETOFINESHIFT]; + + /* Momentum is proportional to distance between the + * object's center of mass and the pivot linedef. + * + * It is scaled by 2^(OVERDRIVE - gear). When gear is + * increased, the momentum gradually decreases to 0 for + * the same amount of pseudotorque, so that oscillations + * are prevented, yet it has a chance to reach equilibrium. + */ + dist = FixedDiv(FixedMul(dist, (mo->gear < OVERDRIVE) ? + y << -(mo->gear - OVERDRIVE) : + y >> +(mo->gear - OVERDRIVE)), x); + + /* Apply momentum away from the pivot linedef. */ + + x = FixedMul(ld->dy, dist); + y = FixedMul(ld->dx, dist); + + /* Avoid moving too fast all of a sudden (step into "overdrive") */ + + dist = FixedMul(x,x) + FixedMul(y,y); + + while (dist > FRACUNIT*4 && mo->gear < MAXGEAR) + ++mo->gear, x >>= 1, y >>= 1, dist >>= 1; + + mo->momx -= x; + mo->momy += y; + } + } + return true; +} + +/* + * killough 9/12/98 + * + * Applies "torque" to objects, based on all contacted linedefs + */ + +void P_ApplyTorque(mobj_t *mo) +{ + int xl = P_GetSafeBlockX((tmbbox[BOXLEFT] = + mo->x - mo->radius) - bmaporgx); + int xh = P_GetSafeBlockX((tmbbox[BOXRIGHT] = + mo->x + mo->radius) - bmaporgx); + int yl = P_GetSafeBlockY((tmbbox[BOXBOTTOM] = + mo->y - mo->radius) - bmaporgy); + int yh = P_GetSafeBlockY((tmbbox[BOXTOP] = + mo->y + mo->radius) - bmaporgy); + int bx,by,flags = mo->intflags; //Remember the current state, for gear-change + + tmthing = mo; + validcount++; /* prevents checking same line twice */ + + for (bx = xl ; bx <= xh ; bx++) + for (by = yl ; by <= yh ; by++) + P_BlockLinesIterator(bx, by, PIT_ApplyTorque); + + /* If any momentum, mark object as 'falling' using engine-internal flags */ + if (mo->momx | mo->momy) + mo->intflags |= MIF_FALLING; + else // Clear the engine-internal flag indicating falling object. + mo->intflags &= ~MIF_FALLING; + + /* If the object has been moving, step up the gear. + * This helps reach equilibrium and avoid oscillations. + * + * Doom has no concept of potential energy, much less + * of rotation, so we have to creatively simulate these + * systems somehow :) + */ + + if (!((mo->intflags | flags) & MIF_FALLING)) // If not falling for a while, + mo->gear = 0; // Reset it to full strength + else + if (mo->gear < MAXGEAR) // Else if not at max gear, + mo->gear++; // move up a gear +} + +// +// P_ThingHeightClip +// Takes a valid thing and adjusts the thing->floorz, +// thing->ceilingz, and possibly thing->z. +// This is called for all nearby monsters +// whenever a sector changes height. +// If the thing doesn't fit, +// the z will be set to the lowest value +// and false will be returned. +// + +dboolean P_ThingHeightClip (mobj_t* thing) +{ + dboolean onfloor; + + onfloor = (thing->z == thing->floorz); + + P_CheckPosition (thing, thing->x, thing->y); + + /* what about stranding a monster partially off an edge? + * killough 11/98: Answer: see below (upset balance if hanging off ledge) + */ + + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->dropoffz = tmdropoffz; /* killough 11/98: remember dropoffs */ + + if (onfloor) + { + + // walking monsters rise and fall with the floor + + thing->z = thing->floorz; + + /* killough 11/98: Possibly upset balance of objects hanging off ledges */ + if (thing->intflags & MIF_FALLING && thing->gear >= MAXGEAR) + thing->gear = 0; + } + else + { + + // don't adjust a floating monster unless forced to + + if (thing->z+thing->height > thing->ceilingz) + thing->z = thing->ceilingz - thing->height; + } + + return thing->ceilingz - thing->floorz >= thing->height; +} + + +// +// SLIDE MOVE +// Allows the player to slide along any angled walls. +// + +/* killough 8/2/98: make variables static */ +static fixed_t bestslidefrac; +static line_t* bestslideline; +static mobj_t* slidemo; +static fixed_t tmxmove; +static fixed_t tmymove; + + +// +// P_HitSlideLine +// Adjusts the xmove / ymove +// so that the next move will slide along the wall. +// If the floor is icy, then you can bounce off a wall. // phares +// + +void P_HitSlideLine (line_t* ld) +{ + int side; + angle_t lineangle; + angle_t moveangle; + angle_t deltaangle; + fixed_t movelen; + fixed_t newlen; + dboolean icyfloor; // is floor icy? // phares + // | + // Under icy conditions, if the angle of approach to the wall // V + // is more than 45 degrees, then you'll bounce and lose half + // your momentum. If less than 45 degrees, you'll slide along + // the wall. 45 is arbitrary and is believable. + + // Check for the special cases of horz or vert walls. + + /* killough 10/98: only bounce if hit hard (prevents wobbling) + * cph - DEMOSYNC - should only affect players in Boom demos? */ + + //e6y + if (mbf_features || prboom_comp[PC_PRBOOM_FRICTION].state) + { + icyfloor = + P_AproxDistance(tmxmove, tmymove) > 4*FRACUNIT && + variable_friction && // killough 8/28/98: calc friction on demand + slidemo->z <= slidemo->floorz && + P_GetFriction(slidemo, NULL) > ORIG_FRICTION; + } + else + { + extern dboolean onground; + icyfloor = !compatibility && + variable_friction && + slidemo->player && + onground && + slidemo->friction > ORIG_FRICTION; + } + + if (ld->slopetype == ST_HORIZONTAL) + { + if (icyfloor && (D_abs(tmymove) > D_abs(tmxmove))) + { + tmxmove /= 2; // absorb half the momentum + tmymove = -tmymove/2; + S_StartSound(slidemo,sfx_oof); // oooff! + } + else + tmymove = 0; // no more movement in the Y direction + return; + } + + if (ld->slopetype == ST_VERTICAL) + { + if (icyfloor && (D_abs(tmxmove) > D_abs(tmymove))) + { + tmxmove = -tmxmove/2; // absorb half the momentum + tmymove /= 2; + S_StartSound(slidemo,sfx_oof); // oooff! // ^ + } // | + else // phares + tmxmove = 0; // no more movement in the X direction + return; + } + + // The wall is angled. Bounce if the angle of approach is // phares + // less than 45 degrees. // phares + + side = P_PointOnLineSide (slidemo->x, slidemo->y, ld); + + lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy); + if (side == 1) + lineangle += ANG180; + moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove); + + // killough 3/2/98: + // The moveangle+=10 breaks v1.9 demo compatibility in + // some demos, so it needs demo_compatibility switch. + + if (!demo_compatibility) + moveangle += 10; // prevents sudden path reversal due to // phares + // rounding error // | + deltaangle = moveangle-lineangle; // V + movelen = P_AproxDistance (tmxmove, tmymove); + if (icyfloor && (deltaangle > ANG45) && (deltaangle < ANG90+ANG45)) + { + moveangle = lineangle - deltaangle; + movelen /= 2; // absorb + S_StartSound(slidemo,sfx_oof); // oooff! + moveangle >>= ANGLETOFINESHIFT; + tmxmove = FixedMul (movelen, finecosine[moveangle]); + tmymove = FixedMul (movelen, finesine[moveangle]); + } // ^ + else // | + { // phares + if (deltaangle > ANG180) + deltaangle += ANG180; + + // I_Error ("SlideLine: ang>ANG180"); + + lineangle >>= ANGLETOFINESHIFT; + deltaangle >>= ANGLETOFINESHIFT; + newlen = FixedMul (movelen, finecosine[deltaangle]); + tmxmove = FixedMul (newlen, finecosine[lineangle]); + tmymove = FixedMul (newlen, finesine[lineangle]); + } // phares +} + + +// +// PTR_SlideTraverse +// + +dboolean PTR_SlideTraverse (intercept_t* in) +{ + line_t* li; + + if (!in->isaline) + I_Error ("PTR_SlideTraverse: not a line?"); + + li = in->d.line; + + if ( ! (li->flags & ML_TWOSIDED) ) + { + if (P_PointOnLineSide (slidemo->x, slidemo->y, li)) + return true; // don't hit the back side + goto isblocking; + } + + // set openrange, opentop, openbottom. + // These define a 'window' from one sector to another across a line + + P_LineOpening (li); + + if (openrange < slidemo->height) + goto isblocking; // doesn't fit + + if (opentop - slidemo->z < slidemo->height) + goto isblocking; // mobj is too high + + if (openbottom - slidemo->z > 24*FRACUNIT ) + goto isblocking; // too big a step up + + // this line doesn't block movement + + return true; + + // the line does block movement, + // see if it is closer than best so far + +isblocking: + + if (in->frac < bestslidefrac) + { + bestslidefrac = in->frac; + bestslideline = li; + } + + return false; // stop +} + + +// +// P_SlideMove +// The momx / momy move is bad, so try to slide +// along a wall. +// Find the first line hit, move flush to it, +// and slide along it +// +// This is a kludgy mess. +// +// killough 11/98: reformatted + +void P_SlideMove(mobj_t *mo) +{ + int hitcount = 3; + + slidemo = mo; // the object that's sliding + + do + { + fixed_t leadx, leady, trailx, traily; + + if (!--hitcount) + goto stairstep; // don't loop forever + + // trace along the three leading corners + + if (mo->momx > 0) + leadx = mo->x + mo->radius, trailx = mo->x - mo->radius; + else + leadx = mo->x - mo->radius, trailx = mo->x + mo->radius; + + if (mo->momy > 0) + leady = mo->y + mo->radius, traily = mo->y - mo->radius; + else + leady = mo->y - mo->radius, traily = mo->y + mo->radius; + + bestslidefrac = FRACUNIT+1; + + P_PathTraverse(leadx, leady, leadx+mo->momx, leady+mo->momy, + PT_ADDLINES, PTR_SlideTraverse); + P_PathTraverse(trailx, leady, trailx+mo->momx, leady+mo->momy, + PT_ADDLINES, PTR_SlideTraverse); + P_PathTraverse(leadx, traily, leadx+mo->momx, traily+mo->momy, + PT_ADDLINES, PTR_SlideTraverse); + + // move up to the wall + + if (bestslidefrac == FRACUNIT+1) + { + // the move must have hit the middle, so stairstep + + stairstep: + + /* killough 3/15/98: Allow objects to drop off ledges + * + * phares 5/4/98: kill momentum if you can't move at all + * This eliminates player bobbing if pressed against a wall + * while on ice. + * + * killough 10/98: keep buggy code around for old Boom demos + * + * cph 2000/09//23: buggy code was only in Boom v2.01 + */ + + if (!P_TryMove(mo, mo->x, mo->y + mo->momy, true)) + if (!P_TryMove(mo, mo->x + mo->momx, mo->y, true)) + if (compatibility_level == boom_201_compatibility) + mo->momx = mo->momy = 0; + + break; + } + + // fudge a bit to make sure it doesn't hit + + if ((bestslidefrac -= 0x800) > 0) + { + fixed_t newx = FixedMul(mo->momx, bestslidefrac); + fixed_t newy = FixedMul(mo->momy, bestslidefrac); + + // killough 3/15/98: Allow objects to drop off ledges + + if (!P_TryMove(mo, mo->x+newx, mo->y+newy, true)) + goto stairstep; + } + + // Now continue along the wall. + // First calculate remainder. + + bestslidefrac = FRACUNIT-(bestslidefrac+0x800); + + if (bestslidefrac > FRACUNIT) + bestslidefrac = FRACUNIT; + + if (bestslidefrac <= 0) + break; + + tmxmove = FixedMul(mo->momx, bestslidefrac); + tmymove = FixedMul(mo->momy, bestslidefrac); + + P_HitSlideLine(bestslideline); // clip the moves + + mo->momx = tmxmove; + mo->momy = tmymove; + + /* killough 10/98: affect the bobbing the same way (but not voodoo dolls) + * cph - DEMOSYNC? */ + if (mo->player && mo->player->mo == mo) + { + if (D_abs(mo->player->momx) > D_abs(tmxmove)) + mo->player->momx = tmxmove; + if (D_abs(mo->player->momy) > D_abs(tmymove)) + mo->player->momy = tmymove; + } + } // killough 3/15/98: Allow objects to drop off ledges: + while (!P_TryMove(mo, mo->x+tmxmove, mo->y+tmymove, true)); +} + +// +// P_LineAttack +// +mobj_t* linetarget; // who got hit (or NULL) +mobj_t* crosshair_target; +static mobj_t* shootthing; + +/* killough 8/2/98: for more intelligent autoaiming */ +static uint_64_t aim_flags_mask; + +// Height if not aiming up or down +fixed_t shootz; + +int la_damage; +fixed_t attackrange; + +static fixed_t aimslope; + +// slopes to top and bottom of target +// killough 4/20/98: make static instead of using ones in p_sight.c + +static fixed_t topslope; +static fixed_t bottomslope; + + +// +// PTR_AimTraverse +// Sets linetaget and aimslope when a target is aimed at. +// +dboolean PTR_AimTraverse (intercept_t* in) +{ + line_t* li; + mobj_t* th; + fixed_t slope; + fixed_t thingtopslope; + fixed_t thingbottomslope; + fixed_t dist; + + if (in->isaline) + { + li = in->d.line; + + if ( !(li->flags & ML_TWOSIDED) ) + return false; // stop + + // Crosses a two sided line. + // A two sided line will restrict + // the possible target ranges. + + P_LineOpening (li); + + if (openbottom >= opentop) + return false; // stop + + dist = FixedMul (attackrange, in->frac); + + // e6y: emulation of missed back side on two-sided lines. + // backsector can be NULL if overrun_missedbackside_emulate is 1 + if (!li->backsector || li->frontsector->floorheight != li->backsector->floorheight) + { + slope = FixedDiv (openbottom - shootz , dist); + if (slope > bottomslope) + bottomslope = slope; + } + + // e6y: emulation of missed back side on two-sided lines. + if (!li->backsector || li->frontsector->ceilingheight != li->backsector->ceilingheight) + { + slope = FixedDiv (opentop - shootz , dist); + if (slope < topslope) + topslope = slope; + } + + if (topslope <= bottomslope) + return false; // stop + + return true; // shot continues + } + + // shoot a thing + + th = in->d.thing; + if (th == shootthing) + return true; // can't shoot self + + if (!(th->flags&MF_SHOOTABLE)) + return true; // corpse or something + + /* killough 7/19/98, 8/2/98: + * friends don't aim at friends (except players), at least not first + */ + if (th->flags & shootthing->flags & aim_flags_mask && !th->player) + return true; + + // check angles to see if the thing can be aimed at + + dist = FixedMul (attackrange, in->frac); + thingtopslope = FixedDiv (th->z+th->height - shootz , dist); + + if (thingtopslope < bottomslope) + return true; // shot over the thing + + thingbottomslope = FixedDiv (th->z - shootz, dist); + + if (thingbottomslope > topslope) + return true; // shot under the thing + + // this thing can be hit! + + if (thingtopslope > topslope) + thingtopslope = topslope; + + if (thingbottomslope < bottomslope) + thingbottomslope = bottomslope; + + aimslope = (thingtopslope+thingbottomslope)/2; + linetarget = th; + + return false; // don't go any farther +} + + +// +// PTR_ShootTraverse +// +dboolean PTR_ShootTraverse (intercept_t* in) +{ + fixed_t x; + fixed_t y; + fixed_t z; + fixed_t frac; + + mobj_t* th; + + fixed_t slope; + fixed_t dist; + fixed_t thingtopslope; + fixed_t thingbottomslope; + + if (in->isaline) + { + line_t *li = in->d.line; + + if (li->special) + P_ShootSpecialLine (shootthing, li); + + if (li->flags & ML_TWOSIDED) + { // crosses a two sided (really 2s) line + P_LineOpening (li); + dist = FixedMul(attackrange, in->frac); + + // killough 11/98: simplify + + // e6y: emulation of missed back side on two-sided lines. + // backsector can be NULL if overrun_missedbackside_emulate is 1 + if (!li->backsector) + { + if ((slope = FixedDiv(openbottom - shootz , dist)) <= aimslope && + (slope = FixedDiv(opentop - shootz , dist)) >= aimslope) + return true; // shot continues + } + else + if ((li->frontsector->floorheight==li->backsector->floorheight || + (slope = FixedDiv(openbottom - shootz , dist)) <= aimslope) && + (li->frontsector->ceilingheight==li->backsector->ceilingheight || + (slope = FixedDiv (opentop - shootz , dist)) >= aimslope)) + return true; // shot continues + } + + // hit line + // position a bit closer + + frac = in->frac - FixedDiv (4*FRACUNIT,attackrange); + x = trace.x + FixedMul (trace.dx, frac); + y = trace.y + FixedMul (trace.dy, frac); + z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange)); + + if (li->frontsector->ceilingpic == skyflatnum) + { + // don't shoot the sky! + + if (z > li->frontsector->ceilingheight) + return false; + + // it's a sky hack wall + + if (li->backsector && li->backsector->ceilingpic == skyflatnum) + + // fix bullet-eaters -- killough: + // WARNING: Almost all demos will lose sync without this + // demo_compatibility flag check!!! killough 1/18/98 + if (demo_compatibility || li->backsector->ceilingheight < z) + return false; + } + + // Spawn bullet puffs. + + P_SpawnPuff (x,y,z); + + // don't go any farther + + return false; + } + + // shoot a thing + + th = in->d.thing; + if (th == shootthing) + return true; // can't shoot self + + if (!(th->flags&MF_SHOOTABLE)) + return true; // corpse or something + + // check angles to see if the thing can be aimed at + + dist = FixedMul (attackrange, in->frac); + thingtopslope = FixedDiv (th->z+th->height - shootz , dist); + + if (thingtopslope < aimslope) + return true; // shot over the thing + + thingbottomslope = FixedDiv (th->z - shootz, dist); + + if (thingbottomslope > aimslope) + return true; // shot under the thing + + // hit thing + // position a bit closer + + frac = in->frac - FixedDiv (10*FRACUNIT,attackrange); + + x = trace.x + FixedMul (trace.dx, frac); + y = trace.y + FixedMul (trace.dy, frac); + z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange)); + + // Spawn bullet puffs or blod spots, + // depending on target type. + if (in->d.thing->flags & MF_NOBLOOD) + P_SpawnPuff (x,y,z); + else + P_SpawnBlood (x,y,z, la_damage, th); + + if (la_damage) + P_DamageMobj (th, shootthing, shootthing, la_damage); + + // don't go any farther + return false; +} + + +// +// P_AimLineAttack +// +fixed_t P_AimLineAttack(mobj_t* t1,angle_t angle,fixed_t distance, uint_64_t mask) +{ + fixed_t x2; + fixed_t y2; + + t1 = P_SubstNullMobj(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 = 100*FRACUNIT/160; + bottomslope = -100*FRACUNIT/160; + + attackrange = distance; + linetarget = NULL; + + /* killough 8/2/98: prevent friends from aiming at friends */ + aim_flags_mask = mask; + + P_PathTraverse(t1->x,t1->y,x2,y2,PT_ADDLINES|PT_ADDTHINGS,PTR_AimTraverse); + + if (linetarget) + return aimslope; + + return 0; +} + + +// +// P_LineAttack +// If damage == 0, it is just a test trace +// that will leave linetarget set. +// + +void P_LineAttack +(mobj_t* t1, + angle_t angle, + fixed_t distance, + fixed_t slope, + int damage) + { + fixed_t x2; + fixed_t y2; + + angle >>= ANGLETOFINESHIFT; + shootthing = t1; + la_damage = damage; + x2 = t1->x + (distance>>FRACBITS)*finecosine[angle]; + y2 = t1->y + (distance>>FRACBITS)*finesine[angle]; + shootz = t1->z + (t1->height>>1) + 8*FRACUNIT; + attackrange = distance; + aimslope = slope; + + P_PathTraverse(t1->x,t1->y,x2,y2,PT_ADDLINES|PT_ADDTHINGS,PTR_ShootTraverse); + } + + +// +// USE LINES +// + +mobj_t* usething; + +dboolean PTR_UseTraverse (intercept_t* in) +{ + int side; + + if (!in->d.line->special) + { + P_LineOpening (in->d.line); + if (openrange <= 0) + { + S_StartSound (usething, sfx_noway); + + // can't use through a wall + return false; + } + + // not a special line, but keep checking + + return true; + } + + side = 0; + if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1) + side = 1; + + // return false; // don't use back side + + P_UseSpecialLine (usething, in->d.line, side, false); + + //WAS can't use for than one special line in a row + //jff 3/21/98 NOW multiple use allowed with enabling line flag + + return (!demo_compatibility && ((in->d.line->flags&ML_PASSUSE) || comperr(comperr_passuse)))?//e6y + true : false; +} + +// Returns false if a "oof" sound should be made because of a blocking +// linedef. Makes 2s middles which are impassable, as well as 2s uppers +// and lowers which block the player, cause the sound effect when the +// player tries to activate them. Specials are excluded, although it is +// assumed that all special linedefs within reach have been considered +// and rejected already (see P_UseLines). +// +// by Lee Killough +// + +dboolean PTR_NoWayTraverse(intercept_t* in) +{ + line_t *ld = in->d.line; + // This linedef + return ld->special || !( // Ignore specials + ld->flags & ML_BLOCKING || ( // Always blocking + P_LineOpening(ld), // Find openings + openrange <= 0 || // No opening + openbottom > usething->z+24*FRACUNIT || // Too high it blocks + opentop < usething->z+usething->height // Too low it blocks + ) + ); +} + +// +// P_UseLines +// Looks for special lines in front of the player to activate. +// +void P_UseLines (player_t* player) +{ + int angle; + fixed_t x1; + fixed_t y1; + fixed_t x2; + fixed_t y2; + + usething = player->mo; + + angle = player->mo->angle >> ANGLETOFINESHIFT; + + x1 = player->mo->x; + y1 = player->mo->y; + x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle]; + y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle]; + + // old code: + // + // P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse ); + // + // This added test makes the "oof" sound work on 2s lines -- killough: + + if (P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse )) + if (!default_comp[comp_sound] && !P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_NoWayTraverse )) + S_StartSound (usething, sfx_noway); +} + + +// +// RADIUS ATTACK +// + +//e6y static +mobj_t *bombsource, *bombspot; +//e6y static +int bombdamage; + + +// +// PIT_RadiusAttack +// "bombsource" is the creature +// that caused the explosion at "bombspot". +// + +dboolean PIT_RadiusAttack (mobj_t* thing) +{ + fixed_t dx; + fixed_t dy; + fixed_t dist; + + /* killough 8/20/98: allow bouncers to take damage + * (missile bouncers are already excluded with MF_NOBLOCKMAP) + */ + + if (!(thing->flags & (MF_SHOOTABLE | MF_BOUNCES))) + return true; + + // Boss spider and cyborg + // take no damage from concussion. + + // killough 8/10/98: allow grenades to hurt anyone, unless + // fired by Cyberdemons, in which case it won't hurt Cybers. + + if (bombspot->flags & MF_BOUNCES ? + thing->type == MT_CYBORG && bombsource->type == MT_CYBORG : + thing->type == MT_CYBORG || thing->type == MT_SPIDER) + return true; + + dx = D_abs(thing->x - bombspot->x); + dy = D_abs(thing->y - bombspot->y); + + dist = dx>dy ? dx : dy; + dist = (dist - thing->radius) >> FRACBITS; + + if (dist < 0) + dist = 0; + + if (dist >= bombdamage) + return true; // out of range + + if ( P_CheckSight (thing, bombspot) ) + { + // must be in direct path + P_DamageMobj (thing, bombspot, bombsource, bombdamage - dist); + } + + return true; +} + + +// +// P_RadiusAttack +// Source is the creature that caused the explosion at spot. +// +void P_RadiusAttack(mobj_t* spot,mobj_t* source,int damage) +{ + int x; + int y; + + int xl; + int xh; + int yl; + int yh; + + fixed_t dist; + + dist = (damage+MAXRADIUS)<y + dist - bmaporgy); + yl = P_GetSafeBlockY(spot->y - dist - bmaporgy); + xh = P_GetSafeBlockX(spot->x + dist - bmaporgx); + xl = P_GetSafeBlockX(spot->x - dist - bmaporgx); + bombspot = spot; + bombsource = source; + bombdamage = damage; + + for (y=yl ; y<=yh ; y++) + for (x=xl ; x<=xh ; x++) + P_BlockThingsIterator (x, y, PIT_RadiusAttack ); +} + + +// +// PIT_ChangeSector +// + +dboolean PIT_ChangeSector (mobj_t* thing) +{ + mobj_t* mo; + + if (P_ThingHeightClip (thing)) + return true; // keep checking + + // crunch bodies to giblets + + if (thing->health <= 0) + { + P_SetMobjState (thing, S_GIBS); + + if (compatibility_level != doom_12_compatibility) + { + thing->flags &= ~MF_SOLID; + } + thing->height = 0; + thing->radius = 0; + if (colored_blood) + { + thing->flags |= MF_COLOREDBLOOD; + thing->bloodcolor = V_BloodColor(thing->info->bloodcolor); + } + return true; // keep checking + } + + // crunch dropped items + + if (thing->flags & MF_DROPPED) + { + P_RemoveMobj (thing); + + // keep checking + return true; + } + + /* killough 11/98: kill touchy things immediately */ + if (thing->flags & MF_TOUCHY && + (thing->intflags & MIF_ARMED || sentient(thing))) + { + P_DamageMobj(thing, NULL, NULL, thing->health); // kill object + return true; // keep checking + } + + if (! (thing->flags & MF_SHOOTABLE) ) + { + // assume it is bloody gibs or something + return true; + } + + nofit = true; + + if (crushchange && !(leveltime&3)) { + int t; + P_DamageMobj(thing,NULL,NULL,10); + + // spray blood in a random direction + mo = P_SpawnMobj (thing->x, + thing->y, + thing->z + thing->height/2, MT_BLOOD); + if (colored_blood) + { + mo->flags |= MF_COLOREDBLOOD; + mo->bloodcolor = V_BloodColor(thing->info->bloodcolor); + } + + /* killough 8/10/98: remove dependence on order of evaluation */ + t = P_Random(pr_crush); + mo->momx = (t - P_Random (pr_crush))<<12; + t = P_Random(pr_crush); + mo->momy = (t - P_Random (pr_crush))<<12; + } + + // keep checking (crush other things) + return true; +} + + +// +// P_ChangeSector +// +dboolean P_ChangeSector(sector_t* sector,dboolean crunch) +{ + int x; + int y; + + nofit = false; + crushchange = crunch; + + // ARRGGHHH!!!! + // This is horrendously slow!!! + // killough 3/14/98 + + // re-check heights for all things near the moving sector + + for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++) + for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++) + P_BlockThingsIterator (x, y, PIT_ChangeSector); + + return nofit; +} + +// +// P_CheckSector +// jff 3/19/98 added to just check monsters on the periphery +// of a moving sector instead of all in bounding box of the +// sector. Both more accurate and faster. +// + +dboolean P_CheckSector(sector_t* sector,dboolean crunch) +{ + msecnode_t *n; + + if (comp[comp_floors]) /* use the old routine for old demos though */ + return P_ChangeSector(sector,crunch); + + nofit = false; + crushchange = crunch; + + // killough 4/4/98: scan list front-to-back until empty or exhausted, + // restarting from beginning after each thing is processed. Avoids + // crashes, and is sure to examine all things in the sector, and only + // the things which are in the sector, until a steady-state is reached. + // Things can arbitrarily be inserted and removed and it won't mess up. + // + // killough 4/7/98: simplified to avoid using complicated counter + + // Mark all things invalid + + for (n=sector->touching_thinglist; n; n=n->m_snext) + n->visited = false; + + do + for (n=sector->touching_thinglist; n; n=n->m_snext) // go through list + if (!n->visited) // unprocessed thing found + { + n->visited = true; // mark thing as processed + if (!(n->m_thing->flags & MF_NOBLOCKMAP)) //jff 4/7/98 don't do these + PIT_ChangeSector(n->m_thing); // process it + break; // exit and start over + } + while (n); // repeat from scratch until all things left are marked valid + + return nofit; +} + + +#define USE_BLOCK_MEMORY_ALLOCATOR +#ifdef USE_BLOCK_MEMORY_ALLOCATOR +// CPhipps - +// Use block memory allocator here + +#include "z_bmalloc.h" + +IMPLEMENT_BLOCK_MEMORY_ALLOC_ZONE(secnodezone, sizeof(msecnode_t), PU_LEVEL, 256, "SecNodes"); + +// +// P_FreeSecNodeList +// +void P_FreeSecNodeList(void) +{ + DECLARE_BLOCK_MEMORY_ALLOC_ZONE(secnodezone); + NULL_BLOCK_MEMORY_ALLOC_ZONE(secnodezone); +} + +inline static msecnode_t* P_GetSecnode(void) +{ + return (msecnode_t*)Z_BMalloc(&secnodezone); +} + +// P_PutSecnode() returns a node to the freelist. + +inline static void P_PutSecnode(msecnode_t* node) +{ + Z_BFree(&secnodezone, node); +} +#else // USE_BLOCK_MEMORY_ALLOCATOR +// phares 3/21/98 +// +// Maintain a freelist of msecnode_t's to reduce memory allocs and frees. + +msecnode_t *headsecnode = NULL; + +// +// P_FreeSecNodeList +// +void P_FreeSecNodeList(void) +{ + headsecnode = NULL; // this is all thats needed to fix the bug +} + +// +// P_GetSecnode +// +// Retrieves a node from the freelist. The calling routine should make sure it +// sets all fields properly. +// +// killough 11/98: reformatted +// +static msecnode_t *P_GetSecnode(void) +{ + msecnode_t *node; + + return headsecnode ? + node = headsecnode, headsecnode = node->m_snext, node : + (msecnode_t *)(Z_Malloc(sizeof *node, PU_LEVEL, NULL)); +} + +// +// P_PutSecnode +// +// Returns a node to the freelist. +// +static void P_PutSecnode(msecnode_t *node) +{ + node->m_snext = headsecnode; + headsecnode = node; +} +#endif // USE_BLOCK_MEMORY_ALLOCATOR + +// phares 3/16/98 +// +// P_AddSecnode() searches the current list to see if this sector is +// already there. If not, it adds a sector node at the head of the list of +// sectors this object appears in. This is called when creating a list of +// nodes that will get linked in later. Returns a pointer to the new node. + +msecnode_t* P_AddSecnode(sector_t* s, mobj_t* thing, msecnode_t* nextnode) +{ + msecnode_t* node; + + node = nextnode; + while (node) + { + if (node->m_sector == s) // Already have a node for this sector? + { + node->m_thing = thing; // Yes. Setting m_thing says 'keep it'. + return(nextnode); + } + node = node->m_tnext; + } + + // Couldn't find an existing node for this sector. Add one at the head + // of the list. + + node = P_GetSecnode(); + + // killough 4/4/98, 4/7/98: mark new nodes unvisited. + node->visited = 0; + + node->m_sector = s; // sector + node->m_thing = thing; // mobj + node->m_tprev = NULL; // prev node on Thing thread + node->m_tnext = nextnode; // next node on Thing thread + if (nextnode) + nextnode->m_tprev = node; // set back link on Thing + + // Add new node at head of sector thread starting at s->touching_thinglist + + node->m_sprev = NULL; // prev node on sector thread + node->m_snext = s->touching_thinglist; // next node on sector thread + if (s->touching_thinglist) + node->m_snext->m_sprev = node; + s->touching_thinglist = node; + return(node); +} + + +// P_DelSecnode() deletes a sector node from the list of +// sectors this object appears in. Returns a pointer to the next node +// on the linked list, or NULL. + +msecnode_t* P_DelSecnode(msecnode_t* node) +{ + msecnode_t* tp; // prev node on thing thread + msecnode_t* tn; // next node on thing thread + msecnode_t* sp; // prev node on sector thread + msecnode_t* sn; // next node on sector thread + + if (node) + { + + // Unlink from the Thing thread. The Thing thread begins at + // sector_list and not from mobj_t->touching_sectorlist. + + tp = node->m_tprev; + tn = node->m_tnext; + if (tp) + tp->m_tnext = tn; + if (tn) + tn->m_tprev = tp; + + // Unlink from the sector thread. This thread begins at + // sector_t->touching_thinglist. + + sp = node->m_sprev; + sn = node->m_snext; + if (sp) + sp->m_snext = sn; + else + node->m_sector->touching_thinglist = sn; + if (sn) + sn->m_sprev = sp; + + // Return this node to the freelist + + P_PutSecnode(node); + return(tn); + } + return(NULL); +} // phares 3/13/98 + +// Delete an entire sector list + +void P_DelSeclist(msecnode_t* node) + + { + while (node) + node = P_DelSecnode(node); + } + + +// phares 3/14/98 +// +// PIT_GetSectors +// Locates all the sectors the object is in by looking at the lines that +// cross through it. You have already decided that the object is allowed +// at this location, so don't bother with checking impassable or +// blocking lines. + +dboolean PIT_GetSectors(line_t* ld) +{ + if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || + tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] || + tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || + tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP]) + return true; + + if (P_BoxOnLineSide(tmbbox, ld) != -1) + return true; + + // This line crosses through the object. + + // Collect the sector(s) from the line and add to the + // sector_list you're examining. If the Thing ends up being + // allowed to move to this position, then the sector_list + // will be attached to the Thing's mobj_t at touching_sectorlist. + + sector_list = P_AddSecnode(ld->frontsector,tmthing,sector_list); + + /* Don't assume all lines are 2-sided, since some Things + * like MT_TFOG are allowed regardless of whether their radius takes + * them beyond an impassable linedef. + * + * killough 3/27/98, 4/4/98: + * Use sidedefs instead of 2s flag to determine two-sidedness. + * killough 8/1/98: avoid duplicate if same sector on both sides + * cph - DEMOSYNC? */ + + if (ld->backsector && ld->backsector != ld->frontsector) + sector_list = P_AddSecnode(ld->backsector, tmthing, sector_list); + + return true; +} + + +// phares 3/14/98 +// +// P_CreateSecNodeList alters/creates the sector_list that shows what sectors +// the object resides in. + +void P_CreateSecNodeList(mobj_t* thing,fixed_t x,fixed_t y) +{ + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + msecnode_t* node; + mobj_t* saved_tmthing = tmthing; /* cph - see comment at func end */ + fixed_t saved_tmx = tmx, saved_tmy = tmy; /* ditto */ + + // First, clear out the existing m_thing fields. As each node is + // added or verified as needed, m_thing will be set properly. When + // finished, delete all nodes where m_thing is still NULL. These + // represent the sectors the Thing has vacated. + + node = sector_list; + while (node) + { + node->m_thing = NULL; + node = node->m_tnext; + } + + tmthing = thing; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + validcount++; // used to make sure we only process a line once + + xl = P_GetSafeBlockX(tmbbox[BOXLEFT] - bmaporgx); + xh = P_GetSafeBlockX(tmbbox[BOXRIGHT] - bmaporgx); + yl = P_GetSafeBlockY(tmbbox[BOXBOTTOM] - bmaporgy); + yh = P_GetSafeBlockY(tmbbox[BOXTOP] - bmaporgy); + + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + P_BlockLinesIterator(bx,by,PIT_GetSectors); + + // Add the sector of the (x,y) point to sector_list. + + sector_list = P_AddSecnode(thing->subsector->sector,thing,sector_list); + + // Now delete any nodes that won't be used. These are the ones where + // m_thing is still NULL. + + node = sector_list; + while (node) + { + if (node->m_thing == NULL) + { + if (node == sector_list) + sector_list = node->m_tnext; + node = P_DelSecnode(node); + } + else + node = node->m_tnext; + } + + /* cph - + * This is the strife we get into for using global variables. tmthing + * is being used by several different functions calling + * P_BlockThingIterator, including functions that can be called *from* + * P_BlockThingIterator. Using a global tmthing is not reentrant. + * OTOH for Boom/MBF demos we have to preserve the buggy behavior. + * Fun. We restore its previous value unless we're in a Boom/MBF demo. + */ + if (!prboom_comp[PC_FORCE_LXDOOM_DEMO_COMPATIBILITY].state) + if ((compatibility_level < boom_compatibility_compatibility) || + (compatibility_level >= prboom_3_compatibility)) + tmthing = saved_tmthing; + /* And, duh, the same for tmx/y - cph 2002/09/22 + * And for tmbbox - cph 2003/08/10 */ + if ((compatibility_level < boom_compatibility_compatibility) /* || + (compatibility_level >= prboom_4_compatibility) */) { + tmx = saved_tmx, tmy = saved_tmy; + if (tmthing) { + tmbbox[BOXTOP] = tmy + tmthing->radius; + tmbbox[BOXBOTTOM] = tmy - tmthing->radius; + tmbbox[BOXRIGHT] = tmx + tmthing->radius; + tmbbox[BOXLEFT] = tmx - tmthing->radius; + } + } +} + +/* cphipps 2004/08/30 - + * Must clear tmthing at tic end, as it might contain a pointer to a removed thinker, or the level might have ended/been ended and we clear the objects it was pointing too. Hopefully we don't need to carry this between tics for sync. */ +void P_MapStart(void) { + if (tmthing) I_Error("P_MapStart: tmthing set!"); +} +void P_MapEnd(void) { + tmthing = NULL; +} diff --git a/src/p_map.h b/src/p_map.h new file mode 100644 index 0000000..7e7ac71 --- /dev/null +++ b/src/p_map.h @@ -0,0 +1,100 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Map functions + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_MAP__ +#define __P_MAP__ + +#include "r_defs.h" +#include "d_player.h" + +#define USERANGE (64*FRACUNIT) +#define MELEERANGE (64*FRACUNIT) +#define MISSILERANGE (32*64*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) + +// killough 3/15/98: add fourth argument to P_TryMove +dboolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, dboolean dropoff); + +// killough 8/9/98: extra argument for telefragging +dboolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y,dboolean boss); +void P_SlideMove(mobj_t *mo); +dboolean P_CheckSight(mobj_t *t1, mobj_t *t2); +void P_UseLines(player_t *player); + +typedef dboolean (*CrossSubsectorFunc)(int num); +extern CrossSubsectorFunc P_CrossSubsector; +dboolean P_CrossSubsector_Doom(int num); +dboolean P_CrossSubsector_Boom(int num); +dboolean P_CrossSubsector_PrBoom(int num); + +// killough 8/2/98: add 'mask' argument to prevent friends autoaiming at others +fixed_t P_AimLineAttack(mobj_t *t1,angle_t angle,fixed_t distance, uint_64_t mask); + +void P_LineAttack(mobj_t *t1, angle_t angle, fixed_t distance, + fixed_t slope, int damage ); +void P_RadiusAttack(mobj_t *spot, mobj_t *source, int damage); +dboolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y); + +//jff 3/19/98 P_CheckSector(): new routine to replace P_ChangeSector() +dboolean P_ChangeSector(sector_t* sector,dboolean crunch); +dboolean P_CheckSector(sector_t *sector, dboolean crunch); +void P_DelSeclist(msecnode_t*); // phares 3/16/98 +void P_FreeSecNodeList(void); // sf +void P_CreateSecNodeList(mobj_t*,fixed_t,fixed_t); // phares 3/14/98 +dboolean Check_Sides(mobj_t *, int, int); // phares + +int P_GetMoveFactor(mobj_t *mo, int *friction); // killough 8/28/98 +int P_GetFriction(const mobj_t *mo, int *factor); // killough 8/28/98 +void P_ApplyTorque(mobj_t *mo); // killough 9/12/98 + +/* cphipps 2004/08/30 */ +void P_MapStart(void); +void P_MapEnd(void); + +// If "floatok" true, move would be ok if within "tmfloorz - tmceilingz". +extern dboolean floatok; +extern dboolean felldown; // killough 11/98: indicates object pushed off ledge +extern fixed_t tmfloorz; +extern fixed_t tmceilingz; +extern line_t *ceilingline; +extern line_t *floorline; // killough 8/23/98 +extern mobj_t *linetarget; // who got hit (or NULL) +extern mobj_t *crosshair_target; +extern msecnode_t *sector_list; // phares 3/16/98 +extern fixed_t tmbbox[4]; // phares 3/20/98 +extern line_t *blockline; // killough 8/11/98 + +#endif // __P_MAP__ diff --git a/src/p_maputl.c b/src/p_maputl.c new file mode 100644 index 0000000..8e48006 --- /dev/null +++ b/src/p_maputl.c @@ -0,0 +1,789 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Movement/collision utility functions, + * as used by function in p_map.c. + * BLOCKMAP Iterator functions, + * and some PIT_* functions to use for iteration. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "doomtype.h" +#include "m_bbox.h" +#include "r_main.h" +#include "p_maputl.h" +#include "p_map.h" +#include "p_setup.h" +#include "lprintf.h" +#include "g_game.h" +#include "g_overflow.h" +#include "e6y.h"//e6y + +// +// P_AproxDistance +// Gives an estimation of distance (not exact) +// + +fixed_t CONSTFUNC P_AproxDistance(fixed_t dx, fixed_t dy) +{ + dx = D_abs(dx); + dy = D_abs(dy); + if (dx < dy) + return dx+dy-(dx>>1); + return dx+dy-(dy>>1); +} + +// +// P_PointOnLineSide +// Returns 0 or 1 +// +// killough 5/3/98: reformatted, cleaned up + +int PUREFUNC P_PointOnLineSide(fixed_t x, fixed_t y, const line_t *line) +{ + return + !line->dx ? x <= line->v1->x ? line->dy > 0 : line->dy < 0 : + !line->dy ? y <= line->v1->y ? line->dx < 0 : line->dx > 0 : + FixedMul(y-line->v1->y, line->dx>>FRACBITS) >= + FixedMul(line->dy>>FRACBITS, x-line->v1->x); +} + +// +// P_BoxOnLineSide +// Considers the line to be infinite +// Returns side 0 or 1, -1 if box crosses the line. +// +// killough 5/3/98: reformatted, cleaned up + +int PUREFUNC P_BoxOnLineSide(const fixed_t *tmbox, const line_t *ld) +{ + switch (ld->slopetype) + { + int p; + default: // shut up compiler warnings -- killough + case ST_HORIZONTAL: + return + (tmbox[BOXBOTTOM] > ld->v1->y) == (p = tmbox[BOXTOP] > ld->v1->y) ? + p ^ (ld->dx < 0) : -1; + case ST_VERTICAL: + return + (tmbox[BOXLEFT] < ld->v1->x) == (p = tmbox[BOXRIGHT] < ld->v1->x) ? + p ^ (ld->dy < 0) : -1; + case ST_POSITIVE: + return + P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld) == + (p = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP], ld)) ? p : -1; + case ST_NEGATIVE: + return + (P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld)) == + (p = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP], ld)) ? p : -1; + } +} + +// +// P_PointOnDivlineSide +// Returns 0 or 1. +// +// killough 5/3/98: reformatted, cleaned up + +int PUREFUNC P_PointOnDivlineSide(fixed_t x, fixed_t y, const divline_t *line) +{ + return + !line->dx ? x <= line->x ? line->dy > 0 : line->dy < 0 : + !line->dy ? y <= line->y ? line->dx < 0 : line->dx > 0 : + (line->dy^line->dx^(x -= line->x)^(y -= line->y)) < 0 ? (line->dy^x) < 0 : + FixedMul(y>>8, line->dx>>8) >= FixedMul(line->dy>>8, x>>8); +} + +// +// P_MakeDivline +// + +void P_MakeDivline(const line_t *li, divline_t *dl) +{ + dl->x = li->v1->x; + dl->y = li->v1->y; + dl->dx = li->dx; + dl->dy = li->dy; +} + +// +// P_InterceptVector +// Returns the fractional intercept point +// along the first divline. +// This is only called by the addthings +// and addlines traversers. +// + +/* cph - this is killough's 4/19/98 version of P_InterceptVector and + * P_InterceptVector2 (which were interchangeable). We still use this + * in compatibility mode. */ +fixed_t PUREFUNC P_InterceptVector2(const divline_t *v2, const divline_t *v1) +{ + fixed_t den; + return (den = FixedMul(v1->dy>>8, v2->dx) - FixedMul(v1->dx>>8, v2->dy)) ? + FixedDiv(FixedMul((v1->x - v2->x)>>8, v1->dy) + + FixedMul((v2->y - v1->y)>>8, v1->dx), den) : 0; +} + +fixed_t PUREFUNC P_InterceptVector(const divline_t *v2, const divline_t *v1) +{ + if (compatibility_level < prboom_4_compatibility) + return P_InterceptVector2(v2, v1); + else { + /* cph - This was introduced at prboom_4_compatibility - no precision/overflow problems */ + int_64_t den = (int_64_t)v1->dy * v2->dx - (int_64_t)v1->dx * v2->dy; + den >>= 16; + if (!den) + return 0; + return (fixed_t)(((int_64_t)(v1->x - v2->x) * v1->dy - (int_64_t)(v1->y - v2->y) * v1->dx) / den); + } +} + +// +// P_LineOpening +// Sets opentop and openbottom to the window +// through a two sided line. +// OPTIMIZE: keep this precalculated +// + +fixed_t opentop; +fixed_t openbottom; +fixed_t openrange; +fixed_t lowfloor; + +// moved front and back outside P-LineOpening and changed // phares 3/7/98 +// them to these so we can pick up the new friction value +// in PIT_CheckLine() +sector_t *openfrontsector; // made global // phares +sector_t *openbacksector; // made global + +void P_LineOpening(const line_t *linedef) +{ + if (linedef->sidenum[1] == NO_INDEX) // single sided line + { + openrange = 0; + return; + } + + openfrontsector = linedef->frontsector; + openbacksector = linedef->backsector; + + if (openfrontsector->ceilingheight < openbacksector->ceilingheight) + opentop = openfrontsector->ceilingheight; + else + opentop = openbacksector->ceilingheight; + + if (openfrontsector->floorheight > openbacksector->floorheight) + { + openbottom = openfrontsector->floorheight; + lowfloor = openbacksector->floorheight; + } + else + { + openbottom = openbacksector->floorheight; + lowfloor = openfrontsector->floorheight; + } + openrange = opentop - openbottom; +} + +// +// THING POSITION SETTING +// + +// +// P_UnsetThingPosition +// Unlinks a thing from block map and sectors. +// On each position change, BLOCKMAP and other +// lookups maintaining lists ot things inside +// these structures need to be updated. +// + +void P_UnsetThingPosition (mobj_t *thing) +{ + if (!(thing->flags & MF_NOSECTOR)) + { + /* invisible things don't need to be in sector list + * unlink from subsector + * + * killough 8/11/98: simpler scheme using pointers-to-pointers for prev + * pointers, allows head node pointers to be treated like everything else + */ + + mobj_t **sprev = thing->sprev; + mobj_t *snext = thing->snext; + if ((*sprev = snext)) // unlink from sector list + snext->sprev = sprev; + + // phares 3/14/98 + // + // Save the sector list pointed to by touching_sectorlist. + // In P_SetThingPosition, we'll keep any nodes that represent + // sectors the Thing still touches. We'll add new ones then, and + // delete any nodes for sectors the Thing has vacated. Then we'll + // put it back into touching_sectorlist. It's done this way to + // avoid a lot of deleting/creating for nodes, when most of the + // time you just get back what you deleted anyway. + // + // If this Thing is being removed entirely, then the calling + // routine will clear out the nodes in sector_list. + + sector_list = thing->touching_sectorlist; + thing->touching_sectorlist = NULL; //to be restored by P_SetThingPosition + } + + if (!(thing->flags & MF_NOBLOCKMAP)) + { + /* inert things don't need to be in blockmap + * + * killough 8/11/98: simpler scheme using pointers-to-pointers for prev + * pointers, allows head node pointers to be treated like everything else + * + * Also more robust, since it doesn't depend on current position for + * unlinking. Old method required computing head node based on position + * at time of unlinking, assuming it was the same position as during + * linking. + */ + + mobj_t *bnext, **bprev = thing->bprev; + if (bprev && (*bprev = bnext = thing->bnext)) // unlink from block map + bnext->bprev = bprev; + } +} + +// +// P_SetThingPosition +// Links a thing into both a block and a subsector +// based on it's x y. +// Sets thing->subsector properly +// +// killough 5/3/98: reformatted, cleaned up + +void P_SetThingPosition(mobj_t *thing) +{ // link into subsector + subsector_t *ss = thing->subsector = R_PointInSubsector(thing->x, thing->y); + if (!(thing->flags & MF_NOSECTOR)) + { + // invisible things don't go into the sector links + + // killough 8/11/98: simpler scheme using pointer-to-pointer prev + // pointers, allows head nodes to be treated like everything else + + mobj_t **link = &ss->sector->thinglist; + mobj_t *snext = *link; + if ((thing->snext = snext)) + snext->sprev = &thing->snext; + thing->sprev = link; + *link = thing; + + // phares 3/16/98 + // + // If sector_list isn't NULL, it has a collection of sector + // nodes that were just removed from this Thing. + + // Collect the sectors the object will live in by looking at + // the existing sector_list and adding new nodes and deleting + // obsolete ones. + + // When a node is deleted, its sector links (the links starting + // at sector_t->touching_thinglist) are broken. When a node is + // added, new sector links are created. + + P_CreateSecNodeList(thing,thing->x,thing->y); + thing->touching_sectorlist = sector_list; // Attach to Thing's mobj_t + sector_list = NULL; // clear for next time + } + + // link into blockmap + if (!(thing->flags & MF_NOBLOCKMAP)) + { + // inert things don't need to be in blockmap + int blockx = P_GetSafeBlockX(thing->x - bmaporgx); + int blocky = P_GetSafeBlockY(thing->y - bmaporgy); + if (blockx>=0 && blockx < bmapwidth && blocky>=0 && blocky < bmapheight) + { + // killough 8/11/98: simpler scheme using pointer-to-pointer prev + // pointers, allows head nodes to be treated like everything else + + mobj_t **link = &blocklinks[blocky*bmapwidth+blockx]; + mobj_t *bnext = *link; + if ((thing->bnext = bnext)) + bnext->bprev = &thing->bnext; + thing->bprev = link; + *link = thing; + } + else // thing is off the map + thing->bnext = NULL, 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_BlockLinesIterator +// The validcount flags are used to avoid checking lines +// that are marked in multiple mapblocks, +// so increment validcount before the first call +// to P_BlockLinesIterator, then make one or more calls +// to it. +// +// killough 5/3/98: reformatted, cleaned up + +dboolean P_BlockLinesIterator(int x, int y, dboolean func(line_t*)) +{ + int offset; + const int *list; // killough 3/1/98: for removal of blockmap limit + + if (x<0 || y<0 || x>=bmapwidth || y>=bmapheight) + return true; + offset = y*bmapwidth+x; + offset = *(blockmap+offset); + list = blockmaplump+offset; // original was reading // phares + // delmiting 0 as linedef 0 // phares + + // killough 1/31/98: for compatibility we need to use the old method. + // Most demos go out of sync, and maybe other problems happen, if we + // don't consider linedef 0. For safety this should be qualified. + + if (!demo_compatibility) // killough 2/22/98: demo_compatibility check + list++; // skip 0 starting delimiter // phares + for ( ; *list != -1 ; list++) // phares + { + line_t *ld; +#ifdef RANGECHECK + if(*list < 0 || *list >= numlines) + I_Error("P_BlockLinesIterator: index >= numlines"); +#endif + ld = &lines[*list]; + if (ld->validcount == validcount) + continue; // line has already been checked + ld->validcount = validcount; + if (!func(ld)) + return false; + } + return true; // everything was checked +} + +// +// P_BlockThingsIterator +// +// killough 5/3/98: reformatted, cleaned up + +dboolean P_BlockThingsIterator(int x, int y, dboolean func(mobj_t*)) +{ + mobj_t *mobj; + if (!(x<0 || y<0 || x>=bmapwidth || y>=bmapheight)) + for (mobj = blocklinks[y*bmapwidth+x]; mobj; mobj = mobj->bnext) + if (!func(mobj)) + return false; + return true; +} + +// +// INTERCEPT ROUTINES +// + +// 1/11/98 killough: Intercept limit removed +intercept_t *intercepts, *intercept_p; + +// Check for limit and double size if necessary -- killough +void check_intercept(void) +{ + static size_t num_intercepts; + size_t offset = intercept_p - intercepts; + if (offset >= num_intercepts) + { + num_intercepts = num_intercepts ? num_intercepts*2 : 128; + intercepts = realloc(intercepts, sizeof(*intercepts)*num_intercepts); + intercept_p = intercepts + offset; + } +} + +divline_t trace; + +// PIT_AddLineIntercepts. +// Looks for lines in the given block +// that intercept the given trace +// to add to the intercepts list. +// +// A line is crossed if its endpoints +// are on opposite sides of the trace. +// +// killough 5/3/98: reformatted, cleaned up + +dboolean PIT_AddLineIntercepts(line_t *ld) +{ + int s1; + int s2; + fixed_t frac; + divline_t dl; + + // avoid precision problems with two routines + if (trace.dx > FRACUNIT*16 || trace.dy > FRACUNIT*16 || + trace.dx < -FRACUNIT*16 || trace.dy < -FRACUNIT*16) + { + s1 = P_PointOnDivlineSide (ld->v1->x, ld->v1->y, &trace); + s2 = P_PointOnDivlineSide (ld->v2->x, ld->v2->y, &trace); + } + else + { + s1 = P_PointOnLineSide (trace.x, trace.y, ld); + s2 = P_PointOnLineSide (trace.x+trace.dx, trace.y+trace.dy, ld); + } + + if (s1 == s2) + return true; // line isn't crossed + + // hit the line + P_MakeDivline(ld, &dl); + frac = P_InterceptVector(&trace, &dl); + + if (frac < 0) + return true; // behind source + + check_intercept(); // killough + + intercept_p->frac = frac; + intercept_p->isaline = true; + intercept_p->d.line = ld; + InterceptsOverrun(intercept_p - intercepts, intercept_p);//e6y + intercept_p++; + + return true; // continue +} + +// +// PIT_AddThingIntercepts +// +// killough 5/3/98: reformatted, cleaned up + +dboolean PIT_AddThingIntercepts(mobj_t *thing) +{ + fixed_t x1, y1; + fixed_t x2, y2; + int s1, s2; + divline_t dl; + fixed_t frac; + + // check a corner to corner crossection for hit + if ((trace.dx ^ trace.dy) > 0) + { + x1 = thing->x - thing->radius; + y1 = thing->y + thing->radius; + x2 = thing->x + thing->radius; + y2 = thing->y - thing->radius; + } + else + { + x1 = thing->x - thing->radius; + y1 = thing->y - thing->radius; + x2 = thing->x + thing->radius; + y2 = thing->y + thing->radius; + } + + s1 = P_PointOnDivlineSide (x1, y1, &trace); + s2 = P_PointOnDivlineSide (x2, y2, &trace); + + if (s1 == s2) + return true; // line isn't crossed + + dl.x = x1; + dl.y = y1; + dl.dx = x2-x1; + dl.dy = y2-y1; + + frac = P_InterceptVector (&trace, &dl); + + if (frac < 0) + return true; // behind source + + check_intercept(); // killough + + intercept_p->frac = frac; + intercept_p->isaline = false; + intercept_p->d.thing = thing; + InterceptsOverrun(intercept_p - intercepts, intercept_p);//e6y + intercept_p++; + + return true; // keep going +} + +// +// P_TraverseIntercepts +// Returns true if the traverser function returns true +// for all lines. +// +// killough 5/3/98: reformatted, cleaned up + +dboolean P_TraverseIntercepts(traverser_t func, fixed_t maxfrac) +{ + intercept_t *in = NULL; + int count = intercept_p - intercepts; + while (count--) + { + fixed_t dist = INT_MAX; + intercept_t *scan; + for (scan = intercepts; scan < intercept_p; scan++) + if (scan->frac < dist) + dist = (in=scan)->frac; + if (dist > maxfrac) + return true; // checked everything in range + if (!func(in)) + return false; // don't bother going farther + in->frac = INT_MAX; + } + return true; // everything was traversed +} + +// +// P_PathTraverse +// Traces a line from x1,y1 to x2,y2, +// calling the traverser function for each. +// Returns true if the traverser function returns true +// for all lines. +// +// killough 5/3/98: reformatted, cleaned up + +dboolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, + int flags, dboolean trav(intercept_t *)) +{ + fixed_t xt1, yt1; + fixed_t xt2, yt2; + fixed_t xstep, ystep; + fixed_t partial; + fixed_t xintercept, yintercept; + int mapx, mapy; + int mapx1, mapy1; + int mapxstep, mapystep; + int count; + + validcount++; + intercept_p = intercepts; + + if (!((x1-bmaporgx)&(MAPBLOCKSIZE-1))) + x1 += FRACUNIT; // don't side exactly on a line + + if (!((y1-bmaporgy)&(MAPBLOCKSIZE-1))) + y1 += FRACUNIT; // don't side exactly on a line + + trace.x = x1; + trace.y = y1; + trace.dx = x2 - x1; + trace.dy = y2 - y1; + + if (comperr(comperr_blockmap)) + { + int_64_t _x1, _x2, _y1, _y2; + + _x1 = (int_64_t)x1 - bmaporgx; + _y1 = (int_64_t)y1 - bmaporgy; + xt1 = (int)(_x1>>MAPBLOCKSHIFT); + yt1 = (int)(_y1>>MAPBLOCKSHIFT); + + mapx1 = (int)(_x1>>MAPBTOFRAC); + mapy1 = (int)(_y1>>MAPBTOFRAC); + + _x2 = (int_64_t)x2 - bmaporgx; + _y2 = (int_64_t)y2 - bmaporgy; + xt2 = (int)(_x2>>MAPBLOCKSHIFT); + yt2 = (int)(_y2>>MAPBLOCKSHIFT); + + x1 -= bmaporgx; + y1 -= bmaporgy; + x2 -= bmaporgx; + y2 -= bmaporgy; + } + else + { + x1 -= bmaporgx; + y1 -= bmaporgy; + xt1 = x1>>MAPBLOCKSHIFT; + yt1 = y1>>MAPBLOCKSHIFT; + + mapx1 = x1>>MAPBTOFRAC; + mapy1 = y1>>MAPBTOFRAC; + + x2 -= bmaporgx; + y2 -= bmaporgy; + xt2 = x2>>MAPBLOCKSHIFT; + yt2 = y2>>MAPBLOCKSHIFT; + } + + if (xt2 > xt1) + { + mapxstep = 1; + partial = FRACUNIT - (mapx1&(FRACUNIT-1)); + ystep = FixedDiv (y2-y1,D_abs(x2-x1)); + } + else + if (xt2 < xt1) + { + mapxstep = -1; + partial = mapx1&(FRACUNIT-1); + ystep = FixedDiv (y2-y1,D_abs(x2-x1)); + } + else + { + mapxstep = 0; + partial = FRACUNIT; + ystep = 256*FRACUNIT; + } + + yintercept = mapy1 + FixedMul(partial, ystep); + + if (yt2 > yt1) + { + mapystep = 1; + partial = FRACUNIT - (mapy1&(FRACUNIT-1)); + xstep = FixedDiv (x2-x1,D_abs(y2-y1)); + } + else + if (yt2 < yt1) + { + mapystep = -1; + partial = mapy1&(FRACUNIT-1); + xstep = FixedDiv (x2-x1,D_abs(y2-y1)); + } + else + { + mapystep = 0; + partial = FRACUNIT; + xstep = 256*FRACUNIT; + } + + xintercept = mapx1 + FixedMul(partial, xstep); + + // Step through map blocks. + // Count is present to prevent a round off error + // from skipping the break. + + mapx = xt1; + mapy = yt1; + + for (count = 0; count < 64; count++) + { + if (flags & PT_ADDLINES) + if (!P_BlockLinesIterator(mapx, mapy,PIT_AddLineIntercepts)) + return false; // early out + + if (flags & PT_ADDTHINGS) + if (!P_BlockThingsIterator(mapx, mapy,PIT_AddThingIntercepts)) + return false; // early out + + if (mapx == xt2 && mapy == yt2) + break; + + if ((yintercept >> FRACBITS) == mapy) + { + yintercept += ystep; + mapx += mapxstep; + } + else + if ((xintercept >> FRACBITS) == mapx) + { + xintercept += xstep; + mapy += mapystep; + } + } + + // go through the sorted list + return P_TraverseIntercepts(trav, FRACUNIT); +} + +// MAES: support 512x512 blockmaps. +int P_GetSafeBlockX(int coord) +{ + coord >>= MAPBLOCKSHIFT; + + // If x is LE than those special values, interpret as positive. + // Otherwise, leave it as it is. + if (comperr(comperr_blockmap) && coord <= blockmapxneg) + return coord & 0x1FF; // Broke width boundary + + return coord; +} + +// MAES: support 512x512 blockmaps. +int P_GetSafeBlockY(int coord) +{ + coord >>= MAPBLOCKSHIFT; + + // If y is LE than those special values, interpret as positive. + // Otherwise, leave it as it is. + if (comperr(comperr_blockmap) && coord <= blockmapyneg) + return coord & 0x1FF; // Broke width boundary + + return coord; +} + +// e6y +// +// 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. + +extern fixed_t bulletslope; + +intercepts_overrun_t 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}, +}; diff --git a/src/p_maputl.h b/src/p_maputl.h new file mode 100644 index 0000000..9dad8f0 --- /dev/null +++ b/src/p_maputl.h @@ -0,0 +1,98 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Map utility functions + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_MAPUTL__ +#define __P_MAPUTL__ + +#include "r_defs.h" + +/* 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) + +#define PT_ADDLINES 1 +#define PT_ADDTHINGS 2 +#define PT_EARLYOUT 4 + +typedef struct { + fixed_t x; + fixed_t y; + fixed_t dx; + fixed_t dy; +} divline_t; + +typedef struct { + fixed_t frac; /* along trace line */ + dboolean isaline; + union { + mobj_t* thing; + line_t* line; + } d; +} intercept_t; + +typedef dboolean (*traverser_t)(intercept_t *in); + +fixed_t CONSTFUNC P_AproxDistance (fixed_t dx, fixed_t dy); +int PUREFUNC P_PointOnLineSide (fixed_t x, fixed_t y, const line_t *line); +int PUREFUNC P_BoxOnLineSide (const fixed_t *tmbox, const line_t *ld); +fixed_t PUREFUNC P_InterceptVector (const divline_t *v2, const divline_t *v1); +/* cph - old compatibility version below */ +fixed_t PUREFUNC P_InterceptVector2(const divline_t *v2, const divline_t *v1); + +extern intercept_t *intercepts, *intercept_p; +void P_MakeDivline(const line_t *li, divline_t *dl); +int PUREFUNC P_PointOnDivlineSide(fixed_t x, fixed_t y, const divline_t *line); +void check_intercept(void); + +void P_LineOpening (const line_t *linedef); +void P_UnsetThingPosition(mobj_t *thing); +void P_SetThingPosition(mobj_t *thing); +dboolean P_BlockLinesIterator (int x, int y, dboolean func(line_t *)); +dboolean P_BlockThingsIterator(int x, int y, dboolean func(mobj_t *)); +dboolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, + int flags, dboolean trav(intercept_t *)); + +// MAES: support 512x512 blockmaps. +int P_GetSafeBlockX(int coord); +int P_GetSafeBlockY(int coord); + +extern fixed_t opentop; +extern fixed_t openbottom; +extern fixed_t openrange; +extern fixed_t lowfloor; +extern divline_t trace; + +#endif /* __P_MAPUTL__ */ diff --git a/src/p_mobj.c b/src/p_mobj.c new file mode 100644 index 0000000..b980096 --- /dev/null +++ b/src/p_mobj.c @@ -0,0 +1,1662 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Moving object handling. Spawn functions. + * + *-----------------------------------------------------------------------------*/ + +#include "doomdef.h" +#include "doomstat.h" +#include "m_random.h" +#include "r_main.h" +#include "p_maputl.h" +#include "p_map.h" +#include "p_tick.h" +#include "sounds.h" +#include "st_stuff.h" +#include "hu_stuff.h" +#include "hu_tracers.h" +#include "s_sound.h" +#include "s_advsound.h" +#include "info.h" +#include "g_game.h" +#include "p_inter.h" +#include "lprintf.h" +#include "r_demo.h" +#include "g_overflow.h" +#include "e6y.h"//e6y + +// [FG] colored blood and gibs +dboolean colored_blood; + +// +// P_SetMobjState +// Returns true if the mobj is still present. +// + +dboolean P_SetMobjState(mobj_t* mobj,statenum_t state) +{ + state_t* st; + + // killough 4/9/98: remember states seen, to detect cycles: + + static statenum_t seenstate_tab[NUMSTATES]; // fast transition table + statenum_t *seenstate = seenstate_tab; // pointer to table + static int recursion; // detects recursion + statenum_t i = state; // initial state + dboolean ret = true; // return value + statenum_t* tempstate = NULL; // for use with recursion + + if (recursion++) // if recursion detected, + seenstate = tempstate = calloc(NUMSTATES, sizeof(statenum_t)); // allocate state table + + do + { + if (state == S_NULL) + { + mobj->state = (state_t *) S_NULL; + P_RemoveMobj (mobj); + ret = false; + break; // killough 4/9/98 + } + + 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) + st->action(mobj); + + seenstate[state] = 1 + st->nextstate; // killough 4/9/98 + + state = st->nextstate; + } while (!mobj->tics && !seenstate[state]); // killough 4/9/98 + + if (ret && !mobj->tics) // killough 4/9/98: detect state cycles + doom_printf("Warning: State Cycle Detected"); + + if (!--recursion) + for (;(state=seenstate[i]);i=state-1) + seenstate[i] = 0; // killough 4/9/98: erase memory of states + + if (tempstate) + free(tempstate); + + return ret; +} + + +// +// P_ExplodeMissile +// + +void P_ExplodeMissile (mobj_t* mo) +{ + mo->momx = mo->momy = mo->momz = 0; + + P_SetMobjState (mo, mobjinfo[mo->type].deathstate); + + mo->tics -= P_Random(pr_explode)&3; + + if (mo->tics < 1) + mo->tics = 1; + + mo->flags &= ~MF_MISSILE; + + if (mo->info->deathsound) + S_StartSound (mo, mo->info->deathsound); +} + + +// +// P_XYMovement +// +// Attempts to move something if it has momentum. +// + +static void P_XYMovement (mobj_t* mo) +{ + player_t *player; + fixed_t xmove, ymove; + + //e6y + fixed_t oldx,oldy; // phares 9/10/98: reducing bobbing/momentum on ice + +#if 0 + fixed_t ptryx; + fixed_t ptryy; + fixed_t xmove; + fixed_t ymove; + fixed_t oldx,oldy; // phares 9/10/98: reducing bobbing/momentum on ice + // when up against walls +#endif + if (!(mo->momx | mo->momy)) // Any momentum? + { + if (mo->flags & MF_SKULLFLY) + { + + // the skull slammed into something + + mo->flags &= ~MF_SKULLFLY; + mo->momz = 0; + + P_SetMobjState (mo, mo->info->spawnstate); + } + return; + } + + player = mo->player; + + if (mo->momx > MAXMOVE) + mo->momx = MAXMOVE; + else if (mo->momx < -MAXMOVE) + mo->momx = -MAXMOVE; + + if (mo->momy > MAXMOVE) + mo->momy = MAXMOVE; + else if (mo->momy < -MAXMOVE) + mo->momy = -MAXMOVE; + + xmove = mo->momx; + ymove = mo->momy; + + oldx = mo->x; // phares 9/10/98: new code to reduce bobbing/momentum + oldy = mo->y; // when on ice & up against wall. These will be compared + // to your x,y values later to see if you were able to move + + do + { + fixed_t ptryx, ptryy; + // killough 8/9/98: fix bug in original Doom source: + // Large negative displacements were never considered. + // This explains the tendency for Mancubus fireballs + // to pass through walls. + // CPhipps - compatibility optioned + + if (xmove > MAXMOVE/2 || ymove > MAXMOVE/2 || + (!comp[comp_moveblock] + && (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; + } + + // killough 3/15/98: Allow objects to drop off + + if (!P_TryMove (mo, ptryx, ptryy, true)) + { + // blocked move + + // killough 8/11/98: bouncing off walls + // killough 10/98: + // Add ability for objects other than players to bounce on ice + + if (!(mo->flags & MF_MISSILE) && + mbf_features && + (mo->flags & MF_BOUNCES || + (!player && blockline && + variable_friction && mo->z <= mo->floorz && + P_GetFriction(mo, NULL) > ORIG_FRICTION))) + { + if (blockline) + { + fixed_t r = ((blockline->dx >> FRACBITS) * mo->momx + + (blockline->dy >> FRACBITS) * mo->momy) / + ((blockline->dx >> FRACBITS)*(blockline->dx >> FRACBITS)+ + (blockline->dy >> FRACBITS)*(blockline->dy >> FRACBITS)); + fixed_t x = FixedMul(r, blockline->dx); + fixed_t y = FixedMul(r, blockline->dy); + + // reflect momentum away from wall + + mo->momx = x*2 - mo->momx; + mo->momy = y*2 - mo->momy; + + // if under gravity, slow down in + // direction perpendicular to wall. + + if (!(mo->flags & MF_NOGRAVITY)) + { + mo->momx = (mo->momx + x)/2; + mo->momy = (mo->momy + y)/2; + } + } + else + mo->momx = mo->momy = 0; + } + else + if (player) // try to slide along it + P_SlideMove (mo); + else + if (mo->flags & MF_MISSILE) + { + // explode a missile + + if (ceilingline && + ceilingline->backsector && + ceilingline->backsector->ceilingpic == skyflatnum) + if (demo_compatibility || // killough + mo->z > ceilingline->backsector->ceilingheight) + { + // Hack to prevent missiles exploding + // against the sky. + // Does not handle sky floors. + + P_RemoveMobj (mo); + return; + } + P_ExplodeMissile (mo); + } + else // whatever else it is, it is now standing still in (x,y) + mo->momx = mo->momy = 0; + } + } while (xmove || ymove); + + // slow down + +#if 0 /* killough 10/98: this is unused code (except maybe in .deh files?) */ + if (player && player->cheats & CF_NOMOMENTUM) + { + // debug option for no sliding at all + mo->momx = mo->momy = 0; + player->momx = player->momy = 0; /* killough 10/98 */ + return; + } +#endif + + /* no friction for missiles or skulls ever, no friction when airborne */ + if (mo->flags & (MF_MISSILE | MF_SKULLFLY)) + return; + + if (mo->z > mo->floorz && !(mo->flags & MF_FLY)) + return; + + /* killough 8/11/98: add bouncers + * killough 9/15/98: add objects falling off ledges + * killough 11/98: only include bouncers hanging off ledges + */ + if (((mo->flags & MF_BOUNCES && mo->z > mo->dropoffz) || + mo->flags & MF_CORPSE || mo->intflags & MIF_FALLING) && + (mo->momx > FRACUNIT/4 || mo->momx < -FRACUNIT/4 || + mo->momy > FRACUNIT/4 || mo->momy < -FRACUNIT/4) && + mo->floorz != mo->subsector->sector->floorheight) + return; // do not stop sliding if halfway off a step with some momentum + + // killough 11/98: + // Stop voodoo dolls that have come to rest, despite any + // moving corresponding player, except in old demos: + + if (mo->momx > -STOPSPEED && mo->momx < STOPSPEED && + mo->momy > -STOPSPEED && mo->momy < STOPSPEED && + (!player || !(player->cmd.forwardmove | player->cmd.sidemove) || + (player->mo != mo && compatibility_level >= lxdoom_1_compatibility))) + { + // if in a walking frame, stop moving + + // killough 10/98: + // Don't affect main player when voodoo dolls stop, except in old demos: + + // COMPAT: MBF compares (demo_version < 203) here, i.e. this should read + // (compatibility_level < lxdoom_1_compatibility) + if (player && (unsigned)(player->mo->state - states - S_PLAY_RUN1) < 4 + && (player->mo == mo || compatibility_level >= lxdoom_1_compatibility)) + P_SetMobjState(player->mo, S_PLAY); + + mo->momx = mo->momy = 0; + + /* killough 10/98: kill any bobbing momentum too (except in voodoo dolls) + * cph - DEMOSYNC - needs compatibility check? + */ + if (player && player->mo == mo) + player->momx = player->momy = 0; + } + else + { + /* phares 3/17/98 + * + * Friction will have been adjusted by friction thinkers for + * icy or muddy floors. Otherwise it was never touched and + * remained set at ORIG_FRICTION + * + * killough 8/28/98: removed inefficient thinker algorithm, + * instead using touching_sectorlist in P_GetFriction() to + * determine friction (and thus only when it is needed). + * + * killough 10/98: changed to work with new bobbing method. + * Reducing player momentum is no longer needed to reduce + * bobbing, so ice works much better now. + * + * cph - DEMOSYNC - need old code for Boom demos? + */ + + //e6y + if (compatibility_level <= boom_201_compatibility && !prboom_comp[PC_PRBOOM_FRICTION].state) + { + // phares 3/17/98 + // Friction will have been adjusted by friction thinkers for icy + // or muddy floors. Otherwise it was never touched and + // remained set at ORIG_FRICTION + mo->momx = FixedMul(mo->momx,mo->friction); + mo->momy = FixedMul(mo->momy,mo->friction); + mo->friction = ORIG_FRICTION; // reset to normal for next tic + } + else if (compatibility_level <= lxdoom_1_compatibility && !prboom_comp[PC_PRBOOM_FRICTION].state) + { + // phares 9/10/98: reduce bobbing/momentum when on ice & up against wall + + if ((oldx == mo->x) && (oldy == mo->y)) // Did you go anywhere? + { // No. Use original friction. This allows you to not bob so much + // if you're on ice, but keeps enough momentum around to break free + // when you're mildly stuck in a wall. + mo->momx = FixedMul(mo->momx,ORIG_FRICTION); + mo->momy = FixedMul(mo->momy,ORIG_FRICTION); + } + else + { // Yes. Use stored friction. + mo->momx = FixedMul(mo->momx,mo->friction); + mo->momy = FixedMul(mo->momy,mo->friction); + } + mo->friction = ORIG_FRICTION; // reset to normal for next tic + } + else + { + + fixed_t friction = P_GetFriction(mo, NULL); + + mo->momx = FixedMul(mo->momx, friction); + mo->momy = FixedMul(mo->momy, friction); + + /* killough 10/98: Always decrease player bobbing by ORIG_FRICTION. + * This prevents problems with bobbing on ice, where it was not being + * reduced fast enough, leading to all sorts of kludges being developed. + */ + + if (player && player->mo == mo) /* Not voodoo dolls */ + { + player->momx = FixedMul(player->momx, ORIG_FRICTION); + player->momy = FixedMul(player->momy, ORIG_FRICTION); + } + + } + + } + +#ifdef GL_DOOM + if (gl_use_motionblur && player == &players[displayplayer]) + { + float dx = (float)(oldx - player->mo->x) / 65536.0f; + float dy = (float)(oldy - player->mo->y) / 65536.0f; + motion_blur.curr_speed_pow2 = dx * dx + dy * dy; + } +#endif +} + + +// +// P_ZMovement +// +// Attempt vertical movement. + +static void P_ZMovement (mobj_t* mo) +{ + /* killough 7/11/98: + * BFG fireballs bounced on floors and ceilings in Pre-Beta Doom + * killough 8/9/98: added support for non-missile objects bouncing + * (e.g. grenade, mine, pipebomb) + */ + + if (mo->flags & MF_BOUNCES && mo->momz) { + mo->z += mo->momz; + if (mo->z <= mo->floorz) { /* bounce off floors */ + mo->z = mo->floorz; + if (mo->momz < 0) { + mo->momz = -mo->momz; + if (!(mo->flags & MF_NOGRAVITY)) { /* bounce back with decay */ + mo->momz = mo->flags & MF_FLOAT ? // floaters fall slowly + mo->flags & MF_DROPOFF ? // DROPOFF indicates rate + FixedMul(mo->momz, (fixed_t)(FRACUNIT*.85)) : + FixedMul(mo->momz, (fixed_t)(FRACUNIT*.70)) : + FixedMul(mo->momz, (fixed_t)(FRACUNIT*.45)) ; + + /* Bring it to rest below a certain speed */ + if (D_abs(mo->momz) <= mo->info->mass*(GRAVITY*4/256)) + mo->momz = 0; + } + + /* killough 11/98: touchy objects explode on impact */ + if (mo->flags & MF_TOUCHY && mo->intflags & MIF_ARMED + && mo->health > 0) + P_DamageMobj(mo, NULL, NULL, mo->health); + else if (mo->flags & MF_FLOAT && sentient(mo)) + goto floater; + return; + } + } else if (mo->z >= mo->ceilingz - mo->height) { + /* bounce off ceilings */ + mo->z = mo->ceilingz - mo->height; + if (mo->momz > 0) { + if (mo->subsector->sector->ceilingpic != skyflatnum) + mo->momz = -mo->momz; /* always bounce off non-sky ceiling */ + else if (mo->flags & MF_MISSILE) + P_RemoveMobj(mo); /* missiles don't bounce off skies */ + else if (mo->flags & MF_NOGRAVITY) + mo->momz = -mo->momz; // bounce unless under gravity + + if (mo->flags & MF_FLOAT && sentient(mo)) + goto floater; + + return; + } + } else { + if (!(mo->flags & MF_NOGRAVITY)) /* free-fall under gravity */ + mo->momz -= mo->info->mass*(GRAVITY/256); + + if (mo->flags & MF_FLOAT && sentient(mo)) goto floater; + return; + } + + /* came to a stop */ + mo->momz = 0; + + if (mo->flags & MF_MISSILE) { + if (ceilingline && + ceilingline->backsector && + ceilingline->backsector->ceilingpic == skyflatnum && + mo->z > ceilingline->backsector->ceilingheight) + P_RemoveMobj(mo); /* don't explode on skies */ + else + P_ExplodeMissile(mo); + } + + if (mo->flags & MF_FLOAT && sentient(mo)) goto floater; + return; + } + + /* killough 8/9/98: end bouncing object code */ + + // check for smooth step up + + if (mo->player && //e6y: restoring original visual behaviour for demo_compatibility + (demo_compatibility || mo->player->mo == mo) && // killough 5/12/98: exclude voodoo dolls + mo->z < mo->floorz) + { + mo->player->viewheight -= mo->floorz-mo->z; + mo->player->deltaviewheight = (VIEWHEIGHT - mo->player->viewheight)>>3; + } + + // adjust altitude + + mo->z += mo->momz; + +floater: + if ((mo->flags & MF_FLOAT) && mo->target) + + // float down towards target if too close + + if (!((mo->flags ^ MF_FLOAT) & (MF_FLOAT | MF_SKULLFLY | MF_INFLOAT)) && + mo->target) /* killough 11/98: simplify */ + { + fixed_t delta; + if (P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y) < + D_abs(delta = mo->target->z + (mo->height>>1) - mo->z)*3) + mo->z += delta < 0 ? -FLOATSPEED : FLOATSPEED; + } + + if (mo->player && (mo->flags & MF_FLY) && (mo->z > mo->floorz)) + { + mo->z += finesine[(FINEANGLES/80*gametic)&FINEMASK]/8; + mo->momz = FixedMul (mo->momz, FRICTION_FLY); + } + + // 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. + * cph - revised 2001/04/15 - + * This was a bug in the Doom/Doom 2 source; the following code + * is meant to make charging lost souls bounce off of floors, but it + * was incorrectly placed after momz was set to 0. + * However, this bug was fixed in Doom95, Final/Ultimate Doom, and + * the v1.10 source release (which is one reason why it failed to sync + * some Doom2 v1.9 demos) + * I've added a comp_soul compatibility option to make this behavior + * selectable for PrBoom v2.3+. For older demos, we do this here only + * if we're in a compatibility level above Doom 2 v1.9 (in which case we + * mimic the bug and do it further down instead) + */ + + if (mo->flags & MF_SKULLFLY && + (!comp[comp_soul] || + (compatibility_level > doom2_19_compatibility && + compatibility_level < prboom_4_compatibility) + )) + mo->momz = -mo->momz; // the skull slammed into something + + if (mo->momz < 0) + { + /* killough 11/98: touchy objects explode on impact */ + if (mo->flags & MF_TOUCHY && mo->intflags & MIF_ARMED && mo->health > 0) + P_DamageMobj(mo, NULL, NULL, mo->health); + else + { + if (mo->player) + mo->player->jumpTics = 7; + if (mo->player && /* killough 5/12/98: exclude voodoo dolls */ + // e6y + // Restoring original visual behaviour for demo_compatibility. + // Viewheight of consoleplayer should be decreased for a moment + // after voodoo doll hits the ground. + // This additional condition makes sense only for plutonia complevel + // when voodoo doll falls down after teleporting, + // but can be applied globally for all demo_compatibility complevels, + // because original sources do not exclude voodoo dolls from condition above, + // but Boom does it. + (demo_compatibility || mo->player->mo == mo) && 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; + //e6y: compatibility optioned + if (default_comp[comp_sound] || (mo->health>0)) /* cph - prevent "oof" when dead */ + S_StartSound (mo, sfx_oof); + } + } + mo->momz = 0; + } + mo->z = mo->floorz; + + /* cph 2001/04/15 - + * This is the buggy lost-soul bouncing code referenced above. + * We've already set momz = 0 normally by this point, so it's useless. + * However we might still have upward momentum, in which case this will + * incorrectly reverse it, so we might still need this for demo sync + */ + if (mo->flags & MF_SKULLFLY && + compatibility_level <= doom2_19_compatibility) + mo->momz = -mo->momz; // the skull slammed into something + + if ( (mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP) ) + { + P_ExplodeMissile (mo); + return; + } + } + else // still above the floor // phares + if (mo->type == MT_GIBDTH && !demorecording && !demoplayback) + { // if (mo->flags & MF_LOGRAV) + // alternate gravity (MF2_LOGRAV from Heretic) + if (mo->momz == 0) + mo->momz = -(GRAVITY >> 3) * 2; + else + mo->momz -= GRAVITY >> 3; + } + else + if (!(mo->flags & MF_NOGRAVITY)) + { + if (!mo->momz) + mo->momz = -GRAVITY; + mo->momz -= GRAVITY; + } + + if (mo->z + mo->height > mo->ceilingz) + { + /* cph 2001/04/15 - + * Lost souls were meant to bounce off of ceilings; + * new comp_soul compatibility option added + */ + if (!comp[comp_soul] && mo->flags & MF_SKULLFLY) + mo->momz = -mo->momz; // the skull slammed into something + + // hit the ceiling + + if (mo->momz > 0) + mo->momz = 0; + + mo->z = mo->ceilingz - mo->height; + + /* cph 2001/04/15 - + * We might have hit a ceiling but had downward momentum (e.g. ceiling is + * lowering on us), so for old demos we must still do the buggy + * momentum reversal here + */ + if (comp[comp_soul] && mo->flags & MF_SKULLFLY) + mo->momz = -mo->momz; // the skull slammed into something + + if ( (mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP) ) + { + P_ExplodeMissile (mo); + return; + } + } + } + +// +// P_NightmareRespawn +// + +static void P_NightmareRespawn(mobj_t* mobj) +{ + fixed_t x; + fixed_t y; + fixed_t z; + subsector_t* ss; + mobj_t* mo; + mapthing_t* mthing; + + x = mobj->spawnpoint.x << FRACBITS; + y = mobj->spawnpoint.y << FRACBITS; + + /* haleyjd: stupid nightmare respawning bug fix + * + * 08/09/00: compatibility added, time to ramble :) + * This fixes the notorious nightmare respawning bug that causes monsters + * that didn't spawn at level startup to respawn at the point (0,0) + * regardless of that point's nature. SMMU and Eternity need this for + * script-spawned things like Halif Swordsmythe, as well. + * + * cph - copied from eternity, alias comp_respawnfix + */ + if(!comp[comp_respawn] && !x && !y) + { + // spawnpoint was zeroed out, so use point of death instead + x = mobj->x; + y = mobj->y; + } + + // something is occupying its position? + + if (!P_CheckPosition (mobj, x, y) ) + return; // no respwan + + // spawn a teleport fog at old spot + // because of removal of the body? + + mo = P_SpawnMobj (mobj->x, + mobj->y, + mobj->subsector->sector->floorheight, + MT_TFOG); + + // initiate teleport sound + + S_StartSound (mo, sfx_telept); + + // spawn a teleport fog at the new spot + + ss = R_PointInSubsector (x,y); + + mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_TFOG); + + S_StartSound (mo, sfx_telept); + + // spawn the new monster + + mthing = &mobj->spawnpoint; + if (mobj->info->flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + // inherit attributes from deceased one + + mo = P_SpawnMobj (x,y,z, mobj->type); + mo->spawnpoint = mobj->spawnpoint; + mo->angle = ANG45 * (mthing->angle/45); + mo->index = mobj->index; + + if (mthing->options & MTF_AMBUSH) + mo->flags |= MF_AMBUSH; + + /* killough 11/98: transfer friendliness from deceased */ + mo->flags = (mo->flags & ~MF_FRIEND) | (mobj->flags & MF_FRIEND); + mo->flags = mo->flags | MF_RESSURECTED;//e6y + + mo->reactiontime = 18; + + // remove the old monster, + + P_RemoveMobj (mobj); +} + + +// +// P_MobjThinker +// + +void P_MobjThinker (mobj_t* mobj) +{ + // killough 11/98: + // removed old code which looked at target references + // (we use pointer reference counting now) + + if (mobj->type == MT_MUSICSOURCE) + { + MusInfoThinker(mobj); + return; + } + + mobj->PrevX = mobj->x; + mobj->PrevY = mobj->y; + mobj->PrevZ = mobj->z; + + CheckThingsHealthTracer(mobj); //e6y + + // momentum movement + if (mobj->momx | mobj->momy || mobj->flags & MF_SKULLFLY) + { + P_XYMovement(mobj); + if (mobj->thinker.function != P_MobjThinker) // cph - Must've been removed + return; // killough - mobj was removed + } + + if (mobj->z != mobj->floorz || mobj->momz) + { + P_ZMovement(mobj); + if (mobj->thinker.function != P_MobjThinker) // cph - Must've been removed + return; // killough - mobj was removed + } + else + if (!(mobj->momx | mobj->momy) && !sentient(mobj)) + { // non-sentient objects at rest + mobj->intflags |= MIF_ARMED; // arm a mine which has come to rest + + // killough 9/12/98: objects fall off ledges if they are hanging off + // slightly push off of ledge if hanging more than halfway off + + if (mobj->z > mobj->dropoffz && // Only objects contacting dropoff + !(mobj->flags & MF_NOGRAVITY) && // Only objects which fall + !comp[comp_falloff]) // Not in old demos + P_ApplyTorque(mobj); // Apply torque + else + mobj->intflags &= ~MIF_FALLING, mobj->gear = 0; // Reset torque + } + + // cycle through states, + // calling action functions at transitions + + if (mobj->tics != -1) + { + mobj->tics--; + + // you can cycle through multiple states in a tic + + if (!mobj->tics) + if (!P_SetMobjState (mobj, mobj->state->nextstate) ) + return; // freed itself + } + else + { + + // check for nightmare respawn + + if (! (mobj->flags & MF_COUNTKILL) ) + return; + + if (!respawnmonsters) + return; + + mobj->movecount++; + + if (mobj->movecount < 12*35) + return; + + if (leveltime & 31) + return; + + if (P_Random (pr_respawn) > 4) + return; + + P_NightmareRespawn (mobj); + } + +} + + +// 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_SubstNullMobj(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; +} + +// +// P_SpawnMobj +// +mobj_t* P_SpawnMobj(fixed_t x,fixed_t y,fixed_t z,mobjtype_t type) +{ + mobj_t* mobj; + state_t* st; + mobjinfo_t* info; + + mobj = Z_Malloc (sizeof(*mobj), PU_LEVEL, 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; // phares + mobj->flags = info->flags; + + /* killough 8/23/98: no friends, bouncers, or touchy things in old demos */ + if (!mbf_features) + mobj->flags &= ~(MF_BOUNCES | MF_FRIEND | MF_TOUCHY); + else + if (type == MT_PLAYER) // Except in old demos, players + mobj->flags |= MF_FRIEND; // are always friends. + + mobj->health = info->spawnhealth; + + if (gameskill != sk_nightmare) + mobj->reactiontime = info->reactiontime; + + mobj->lastlook = P_Random (pr_lastlook) % MAXPLAYERS; + + // do not set the state with P_SetMobjState, + // because action routines can not be called yet + + st = &states[info->spawnstate]; + + mobj->state = st; + mobj->tics = st->tics; + mobj->sprite = st->sprite; + mobj->frame = st->frame; + mobj->touching_sectorlist = NULL; // NULL head of sector list // phares 3/13/98 + + // set subsector and/or block links + + P_SetThingPosition (mobj); + + mobj->dropoffz = /* killough 11/98: for tracking dropoffs */ + mobj->floorz = mobj->subsector->sector->floorheight; + mobj->ceilingz = mobj->subsector->sector->ceilingheight; + + mobj->z = z == ONFLOORZ ? mobj->floorz : z == ONCEILINGZ ? + mobj->ceilingz - mobj->height : z; + + mobj->PrevX = mobj->x; + mobj->PrevY = mobj->y; + mobj->PrevZ = mobj->z; + + mobj->thinker.function = P_MobjThinker; + + //e6y + mobj->friction = ORIG_FRICTION; // phares 3/17/98 + mobj->index = -1; + + mobj->target = mobj->tracer = mobj->lastenemy = NULL; + P_AddThinker (&mobj->thinker); + if (!((mobj->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) + totallive++; + return mobj; +} + + +static mapthing_t itemrespawnque[ITEMQUESIZE]; +static int itemrespawntime[ITEMQUESIZE]; +int iquehead; +int iquetail; + + +// +// P_RemoveMobj +// + +void P_RemoveMobj (mobj_t* mobj) +{ + if ((mobj->flags & MF_SPECIAL) + && !(mobj->flags & MF_DROPPED) + && (mobj->type != MT_INV) + && (mobj->type != MT_INS)) + { + itemrespawnque[iquehead] = mobj->spawnpoint; + itemrespawntime[iquehead] = leveltime; + iquehead = (iquehead+1)&(ITEMQUESIZE-1); + + // lose one off the end? + + if (iquehead == iquetail) + iquetail = (iquetail+1)&(ITEMQUESIZE-1); + } + + // unlink from sector and block lists + + P_UnsetThingPosition (mobj); + + // Delete all nodes on the current sector_list phares 3/16/98 + + if (sector_list) + { + P_DelSeclist(sector_list); + sector_list = NULL; + } + + // stop any playing sound + + // [FG] removed map objects may finish their sounds + if (full_sounds) + S_UnlinkSound(mobj); + else + S_StopSound (mobj); + + // killough 11/98: + // + // Remove any references to other mobjs. + // + // Older demos might depend on the fields being left alone, however, + // if multiple thinkers reference each other indirectly before the + // end of the current tic. + // CPhipps - only leave dead references in old demos; I hope lxdoom_1 level + // demos are rare and don't rely on this. I hope. + + if ((compatibility_level >= lxdoom_1_compatibility) || + (!demorecording && !demoplayback)) { + P_SetTarget(&mobj->target, NULL); + P_SetTarget(&mobj->tracer, NULL); + P_SetTarget(&mobj->lastenemy, NULL); + } + // free block + + P_RemoveThinker (&mobj->thinker); +} + + +/* + * P_FindDoomedNum + * + * Finds a mobj type with a matching doomednum + * + * killough 8/24/98: rewrote to use hashing + */ + +static PUREFUNC int P_FindDoomedNum(unsigned type) +{ + static struct { int first, next; } *hash; + register int i; + + if (!hash) + { + hash = Z_Malloc(sizeof *hash * NUMMOBJTYPES, PU_CACHE, (void **) &hash); + for (i=0; ix << FRACBITS; + y = mthing->y << FRACBITS; + + // spawn a teleport fog at the new spot + + ss = R_PointInSubsector (x,y); + mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_IFOG); + S_StartSound (mo, sfx_itmbk); + + // find which type to spawn + + /* killough 8/23/98: use table for faster lookup */ + i = P_FindDoomedNum(mthing->type); + + // spawn it + + if (mobjinfo[i].flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + mo = P_SpawnMobj (x,y,z, i); + mo->spawnpoint = *mthing; + mo->angle = ANG45 * (mthing->angle/45); + + // pull it from the queue + + iquetail = (iquetail+1)&(ITEMQUESIZE-1); +} + +// +// P_SpawnPlayer +// Called when a player is spawned on the level. +// Most of the player structure stays unchanged +// between levels. +// + +extern byte playernumtotrans[MAXPLAYERS]; + +void P_SpawnPlayer (int n, const mapthing_t* mthing) +{ + player_t* p; + fixed_t x; + fixed_t y; + fixed_t z; + mobj_t* mobj; + int i; + + // e6y + // playeringame overflow detection + // it detects and emulates overflows on vex6d.wad\bug_wald(toke).lmp, etc. + // http://www.doom2.net/doom2/research/runningbody.zip + if (PlayeringameOverrun(mthing)) + return; + + // not playing? + + if (!playeringame[n]) + return; + + p = &players[n]; + + if (p->playerstate == PST_REBORN) + G_PlayerReborn (n); + + /* cph 2001/08/14 - use the options field of memorised player starts to + * indicate whether the start really exists in the level. + */ + if (!mthing->options) + I_Error("P_SpawnPlayer: attempt to spawn player at unavailable start point"); + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + z = ONFLOORZ; + mobj = P_SpawnMobj (x,y,z, MT_PLAYER); + + if (deathmatch) + mobj->index = TracerGetDeathmatchStart(n); + else + mobj->index = TracerGetPlayerStart(mthing->type - 1); + + + // set color translations for player sprites + + mobj->flags |= playernumtotrans[n]<angle = ANG45 * (mthing->angle/45); + mobj->player = p; + mobj->health = p->health; + mobj->player->prev_viewangle = mobj->angle + viewangleoffset; + + 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->momx = p->momy = 0; // killough 10/98: initialize bobbing to 0. + + // setup gun psprite + + P_SetupPsprites (p); + + // give all cards in death match mode + + if (deathmatch) + for (i = 0 ; i < NUMCARDS ; i++) + p->cards[i] = true; + + if (mthing->type-1 == consoleplayer) + { + ST_Start(); // wake up the status bar + HU_Start(); // wake up the heads up text + } + R_SmoothPlaying_Reset(p); // e6y +} + +/* + * P_IsDoomnumAllowed() + * Based on code taken from P_LoadThings() in src/p_setup.c Return TRUE + * if the thing in question is expected to be available in the gamemode used. + */ + +dboolean P_IsDoomnumAllowed(int doomnum) +{ + // Do not spawn cool, new monsters if !commercial + if (gamemode != commercial) + switch(doomnum) + { + case 64: // Archvile + case 65: // Former Human Commando + case 66: // Revenant + case 67: // Mancubus + case 68: // Arachnotron + case 69: // Hell Knight + case 71: // Pain Elemental + case 84: // Wolf SS + case 88: // Boss Brain + case 89: // Boss Shooter + return false; + } + + return true; +} + +// +// P_SpawnMapThing +// The fields of the mapthing should +// already be in host byte order. +// + +mobj_t* P_SpawnMapThing (const mapthing_t* mthing, int index) +{ + int i; + //int bit; + mobj_t* mobj; + fixed_t x; + fixed_t y; + fixed_t z; + int options = mthing->options; /* cph 2001/07/07 - make writable copy */ + short thingtype = mthing->type; + int iden_num = 0; + + // killough 2/26/98: Ignore type-0 things as NOPs + // phares 5/14/98: Ignore Player 5-8 starts (for now) + + switch(thingtype) + { + case 0: + case DEN_PLAYER5: + case DEN_PLAYER6: + case DEN_PLAYER7: + case DEN_PLAYER8: + return NULL; + } + + // killough 11/98: clear flags unused by Doom + // + // We clear the flags unused in Doom if we see flag mask 256 set, since + // it is reserved to be 0 under the new scheme. A 1 in this reserved bit + // indicates it's a Doom wad made by a Doom editor which puts 1's in + // bits that weren't used in Doom (such as HellMaker wads). So we should + // then simply ignore all upper bits. + + if (demo_compatibility || + (compatibility_level >= lxdoom_1_compatibility && + options & MTF_RESERVED)) { + if (!demo_compatibility) // cph - Add warning about bad thing flags + lprintf(LO_WARN, "P_SpawnMapThing: correcting bad flags (%u) (thing type %d)\n", + options, thingtype); + options &= MTF_EASY|MTF_NORMAL|MTF_HARD|MTF_AMBUSH|MTF_NOTSINGLE; + } + + // count deathmatch start positions + + // doom2.exe has at most 10 deathmatch starts + if (thingtype == 11) + { + if (!(!compatibility || deathmatch_p-deathmatchstarts < 10)) { + return NULL; + } else { + // 1/11/98 killough -- new code removes limit on deathmatch starts: + + size_t offset = deathmatch_p - deathmatchstarts; + + if (compatibility && deathmatch_p-deathmatchstarts >= 10) + return NULL; //e6y + if (offset >= num_deathmatchstarts) + { + num_deathmatchstarts = num_deathmatchstarts ? + num_deathmatchstarts*2 : 16; + deathmatchstarts = realloc(deathmatchstarts, + num_deathmatchstarts * + sizeof(*deathmatchstarts)); + deathmatch_p = deathmatchstarts + offset; + } + memcpy(deathmatch_p++, mthing, sizeof(*mthing)); + (deathmatch_p-1)->options = 1; + + TracerAddDeathmatchStart(deathmatch_p - deathmatchstarts - 1, index); + + return NULL; + } + } + + // check for players specially + + if (thingtype <= 4 && thingtype > 0) // killough 2/26/98 -- fix crashes + { + // killough 7/19/98: Marine's best friend :) + if (!netgame && thingtype > 1 && thingtype <= dogs+1 && + !players[thingtype-1].secretcount) + { // use secretcount to avoid multiple dogs in case of multiple starts + players[thingtype-1].secretcount = 1; + + // killough 10/98: force it to be a friend + options |= MTF_FRIEND; + if(HelperThing != -1) // haleyjd 9/22/99: deh substitution + { + int type = HelperThing - 1; + if(type >= 0 && type < NUMMOBJTYPES) + { + i = type; + } + else + { + doom_printf("Invalid value %i for helper, ignored.", HelperThing); + i = MT_DOGS; + } + } + else { + i = MT_DOGS; + } + goto spawnit; + } + + // save spots for respawning in coop games + playerstarts[thingtype-1] = *mthing; + /* cph 2006/07/24 - use the otherwise-unused options field to flag that + * this start is present (so we know which elements of the array are filled + * in, in effect). Also note that the call below to P_SpawnPlayer must use + * the playerstarts version with this field set */ + playerstarts[thingtype-1].options = 1; + + TracerAddPlayerStart(thingtype - 1, index); + + if (!deathmatch) + P_SpawnPlayer (thingtype-1, &playerstarts[thingtype-1]); + return NULL; + } + + // check for apropriate skill level + + /* jff "not single" thing flag */ + if (!coop_spawns && !netgame && options & MTF_NOTSINGLE) + return NULL; + + //jff 3/30/98 implement "not deathmatch" thing flag + + if (netgame && deathmatch && options & MTF_NOTDM) + return NULL; + + //jff 3/30/98 implement "not cooperative" thing flag + + if ((coop_spawns || netgame) && !deathmatch && options & MTF_NOTCOOP) + return NULL; + + // killough 11/98: simplify + if (gameskill == sk_baby || gameskill == sk_easy ? + !(options & MTF_EASY) : + gameskill == sk_hard || gameskill == sk_nightmare ? + !(options & MTF_HARD) : !(options & MTF_NORMAL)) + return NULL; + + if (thingtype >= 14100 && thingtype <= 14164) + { + // Use the ambient number + iden_num = thingtype - 14100; // Mus change + thingtype = 14164; // MT_MUSICSOURCE + } + + // find which type to spawn + + // killough 8/23/98: use table for faster lookup + i = P_FindDoomedNum(thingtype); + + // phares 5/16/98: + // Do not abort because of an unknown thing. Ignore it, but post a + // warning message for the player. + + if (i == NUMMOBJTYPES) + { + lprintf(LO_INFO, "P_SpawnMapThing: Unknown Thing type %i at (%i, %i)\n", thingtype, mthing->x, mthing->y); + return NULL; + } + + // don't spawn keycards and players in deathmatch + + if (deathmatch && mobjinfo[i].flags & MF_NOTDMATCH) + return NULL; + + // don't spawn any monsters if -nomonsters + + if (nomonsters && (i == MT_SKULL || (mobjinfo[i].flags & MF_COUNTKILL))) + return NULL; + + // spawn it +spawnit: + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + if (mobjinfo[i].flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + mobj = P_SpawnMobj (x,y,z, i); + mobj->spawnpoint = *mthing; + mobj->index = index;//e6y + mobj->iden_nums = iden_num; + + if (mobj->tics > 0) + mobj->tics = 1 + (P_Random (pr_spawnthing) % mobj->tics); + + if (!(mobj->flags & MF_FRIEND) && + options & MTF_FRIEND && + mbf_features) + { + mobj->flags |= MF_FRIEND; // killough 10/98: + P_UpdateThinker(&mobj->thinker); // transfer friendliness flag + } + + /* killough 7/20/98: exclude friends */ + if (!((mobj->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) + totalkills++; + + if (mobj->flags & MF_COUNTITEM) + totalitems++; + + mobj->angle = ANG45 * (mthing->angle/45); + if (options & MTF_AMBUSH) + mobj->flags |= MF_AMBUSH; + + // RjY + // Print a warning when a solid hanging body is used in a sector where + // the player can walk under it, to help people with map debugging + if (!((~mobj->flags) & (MF_SOLID | MF_SPAWNCEILING)) // solid and hanging + // invert everything, then both bits should be clear + && mobj->floorz + mobjinfo[MT_PLAYER].height <= mobj->z) // head <= base + // player under body's head height <= bottom of body + { + lprintf(LO_WARN, "P_SpawnMapThing: solid hanging body in tall sector at " + "%d,%d (type=%d)\n", mthing->x, mthing->y, thingtype); + } + + return mobj; +} + +// +// GAME SPAWN FUNCTIONS +// + +// +// P_SpawnPuff +// + +extern fixed_t attackrange; + +void P_SpawnPuff(fixed_t x,fixed_t y,fixed_t z) +{ + mobj_t* th; + // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_spawnpuff); + z += (t - P_Random(pr_spawnpuff))<<10; + + th = P_SpawnMobj (x,y,z, MT_PUFF); + th->momz = FRACUNIT; + th->tics -= P_Random(pr_spawnpuff)&3; + + if (th->tics < 1) + th->tics = 1; + + // don't make punches spark on the wall + + if (attackrange == MELEERANGE) + P_SetMobjState (th, S_PUFF3); +} + + + +// +// P_SpawnBlood +// +void P_SpawnBlood(fixed_t x,fixed_t y,fixed_t z,int damage, mobj_t* bleeder) +{ + mobj_t* th; + // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_spawnblood); + z += (t - P_Random(pr_spawnblood))<<10; + th = P_SpawnMobj(x,y,z, MT_BLOOD); + th->momz = FRACUNIT*2; + th->tics -= P_Random(pr_spawnblood)&3; + if (colored_blood) + { + th->flags |= MF_COLOREDBLOOD; + th->bloodcolor = V_BloodColor(bleeder->info->bloodcolor); + } + + if (th->tics < 1) + th->tics = 1; + + if (damage <= 12 && damage >= 9) + P_SetMobjState (th,S_BLOOD2); + else if (damage < 9) + P_SetMobjState (th,S_BLOOD3); +} + + +// +// P_CheckMissileSpawn +// Moves the missile forward a bit +// and possibly explodes it right there. +// + +void P_CheckMissileSpawn (mobj_t* th) +{ + th->tics -= P_Random(pr_missile)&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); + + // killough 8/12/98: for non-missile objects (e.g. grenades) + if (!(th->flags & MF_MISSILE) && mbf_features) + return; + + // killough 3/15/98: no dropoff (really = don't care for missiles) + + if (!P_TryMove (th, th->x, th->y, false)) + P_ExplodeMissile (th); +} + + +// +// P_SpawnMissile +// + +mobj_t* P_SpawnMissile(mobj_t* source,mobj_t* dest,mobjtype_t type) +{ + mobj_t* th; + angle_t an; + int dist; + + th = P_SpawnMobj (source->x,source->y,source->z + 4*8*FRACUNIT,type); + + if (th->info->seesound) + S_StartSound (th, th->info->seesound); + + P_SetTarget(&th->target, source); // where it came from + an = R_PointToAngle2 (source->x, source->y, dest->x, dest->y); + + // fuzzy player + + if (dest->flags & MF_SHADOW) + { // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_shadow); + an += (t - P_Random(pr_shadow))<<20; + } + + th->angle = an; + an >>= ANGLETOFINESHIFT; + th->momx = FixedMul (th->info->speed, finecosine[an]); + th->momy = FixedMul (th->info->speed, finesine[an]); + + dist = P_AproxDistance (dest->x - source->x, dest->y - source->y); + dist = dist / th->info->speed; + + if (dist < 1) + dist = 1; + + th->momz = (dest->z - source->z) / dist; + P_CheckMissileSpawn (th); + + return th; +} + + +// +// P_SpawnPlayerMissile +// Tries to aim at a nearby monster +// + +void P_SpawnPlayerMissile(mobj_t* source,mobjtype_t type) +{ + mobj_t *th; + fixed_t x, y, z, slope = 0; + + // see which target is to be aimed at + + angle_t an = source->angle; + + // killough 7/19/98: autoaiming was not in original beta + if (comperr(comperr_freeaim)) + slope = finetangent[(ANG90 - source->pitch) >> ANGLETOFINESHIFT]; + else + { + // killough 8/2/98: prefer autoaiming at enemies + uint_64_t mask = mbf_features ? MF_FRIEND : 0; + + do + { + slope = P_AimLineAttack(source, an, 16*64*FRACUNIT, mask); + if (!linetarget) + slope = P_AimLineAttack(source, an += 1<<26, 16*64*FRACUNIT, mask); + if (!linetarget) + slope = P_AimLineAttack(source, an -= 2<<26, 16*64*FRACUNIT, mask); + if (!linetarget) + an = source->angle, slope = 0; + } + while (mask && (mask=0, !linetarget)); // killough 8/2/98 + } + + x = source->x; + y = source->y; + z = source->z + 4*8*FRACUNIT; + + th = P_SpawnMobj (x,y,z, type); + + if (th->info->seesound) + S_StartSound (th, th->info->seesound); + + P_SetTarget(&th->target, source); + th->angle = an; + th->momx = FixedMul(th->info->speed,finecosine[an>>ANGLETOFINESHIFT]); + th->momy = FixedMul(th->info->speed,finesine[an>>ANGLETOFINESHIFT]); + th->momz = FixedMul(th->info->speed,slope); + + P_CheckMissileSpawn(th); + } diff --git a/src/p_mobj.h b/src/p_mobj.h new file mode 100644 index 0000000..5c24df4 --- /dev/null +++ b/src/p_mobj.h @@ -0,0 +1,428 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Map Objects, MObj, definition and handling. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_MOBJ__ +#define __P_MOBJ__ + +// Basics. +#include "tables.h" +#include "m_fixed.h" + +// We need the thinker_t stuff. +#include "d_think.h" + +// We need the WAD data structure for Map things, +// from the THINGS lump. +#include "doomdata.h" + +// States are tied to finite states are +// tied to animation frames. +// Needs precompiled tables/data structures. +#include "info.h" + +// +// NOTES: mobj_t +// +// mobj_ts are used to tell the refresh where to draw an image, +// tell the world simulation when objects are contacted, +// and tell the sound driver how to position a sound. +// +// The refresh uses the next and prev links to follow +// lists of things in sectors as they are being drawn. +// The sprite, frame, and angle elements determine which patch_t +// is used to draw the sprite if it is visible. +// The sprite and frame values are allmost allways set +// from state_t structures. +// The statescr.exe utility generates the states.h and states.c +// files that contain the sprite/frame numbers from the +// statescr.txt source file. +// The xyz origin point represents a point at the bottom middle +// of the sprite (between the feet of a biped). +// This is the default origin position for patch_ts grabbed +// with lumpy.exe. +// A walking creature will have its z equal to the floor +// it is standing on. +// +// The sound code uses the x,y, and subsector fields +// to do stereo positioning of any sound effited by the mobj_t. +// +// The play simulation uses the blocklinks, x,y,z, radius, height +// to determine when mobj_ts are touching each other, +// touching lines in the map, or hit by trace lines (gunshots, +// lines of sight, etc). +// The mobj_t->flags element has various bit flags +// used by the simulation. +// +// Every mobj_t is linked into a single sector +// based on its origin coordinates. +// The subsector_t is found with R_PointInSubsector(x,y), +// and the sector_t can be found with subsector->sector. +// The sector links are only used by the rendering code, +// the play simulation does not care about them at all. +// +// Any mobj_t that needs to be acted upon by something else +// in the play world (block movement, be shot, etc) will also +// need to be linked into the blockmap. +// If the thing has the MF_NOBLOCK flag set, it will not use +// the block links. It can still interact with other things, +// but only as the instigator (missiles will run into other +// things, but nothing can run into a missile). +// Each block in the grid is 128*128 units, and knows about +// every line_t that it contains a piece of, and every +// interactable mobj_t that has its origin contained. +// +// A valid mobj_t is a mobj_t that has the proper subsector_t +// filled in for its xy coordinates and is linked into the +// sector from which the subsector was made, or has the +// MF_NOSECTOR flag set (the subsector_t needs to be valid +// even if MF_NOSECTOR is set), and is linked into a blockmap +// block or has the MF_NOBLOCKMAP flag set. +// Links should only be modified by the P_[Un]SetThingPosition() +// functions. +// Do not change the MF_NO? flags while a thing is valid. +// +// Any questions? +// + +// +// Misc. mobj flags +// + +// Call P_SpecialThing when touched. +#define MF_SPECIAL (uint_64_t)(0x0000000000000001) +// Blocks. +#define MF_SOLID (uint_64_t)(0x0000000000000002) +// Can be hit. +#define MF_SHOOTABLE (uint_64_t)(0x0000000000000004) +// Don't use the sector links (invisible but touchable). +#define MF_NOSECTOR (uint_64_t)(0x0000000000000008) +// Don't use the blocklinks (inert but displayable) +#define MF_NOBLOCKMAP (uint_64_t)(0x0000000000000010) + +// Not to be activated by sound, deaf monster. +#define MF_AMBUSH (uint_64_t)(0x0000000000000020) +// Will try to attack right back. +#define MF_JUSTHIT (uint_64_t)(0x0000000000000040) +// Will take at least one step before attacking. +#define MF_JUSTATTACKED (uint_64_t)(0x0000000000000080) +// On level spawning (initial position), +// hang from ceiling instead of stand on floor. +#define MF_SPAWNCEILING (uint_64_t)(0x0000000000000100) +// Don't apply gravity (every tic), +// that is, object will float, keeping current height +// or changing it actively. +#define MF_NOGRAVITY (uint_64_t)(0x0000000000000200) + +// Movement flags. +// This allows jumps from high places. +#define MF_DROPOFF (uint_64_t)(0x0000000000000400) +// For players, will pick up items. +#define MF_PICKUP (uint_64_t)(0x0000000000000800) +// Player cheat. ??? +#define MF_NOCLIP (uint_64_t)(0x0000000000001000) +// Player: keep info about sliding along walls. +#define MF_SLIDE (uint_64_t)(0x0000000000002000) +// Allow moves to any height, no gravity. +// For active floaters, e.g. cacodemons, pain elementals. +#define MF_FLOAT (uint_64_t)(0x0000000000004000) +// Don't cross lines +// ??? or look at heights on teleport. +#define MF_TELEPORT (uint_64_t)(0x0000000000008000) +// Don't hit same species, explode on block. +// Player missiles as well as fireballs of various kinds. +#define MF_MISSILE (uint_64_t)(0x0000000000010000) +// Dropped by a demon, not level spawned. +// E.g. ammo clips dropped by dying former humans. +#define MF_DROPPED (uint_64_t)(0x0000000000020000) +// Use fuzzy draw (shadow demons or spectres), +// temporary player invisibility powerup. +#define MF_SHADOW (uint_64_t)(0x0000000000040000) +// Flag: don't bleed when shot (use puff), +// barrels and shootable furniture shall not bleed. +#define MF_NOBLOOD (uint_64_t)(0x0000000000080000) +// Don't stop moving halfway off a step, +// that is, have dead bodies slide down all the way. +#define MF_CORPSE (uint_64_t)(0x0000000000100000) +// Floating to a height for a move, ??? +// don't auto float to target's height. +#define MF_INFLOAT (uint_64_t)(0x0000000000200000) + +// On kill, count this enemy object +// towards intermission kill total. +// Happy gathering. +#define MF_COUNTKILL (uint_64_t)(0x0000000000400000) + +// On picking up, count this item object +// towards intermission item total. +#define MF_COUNTITEM (uint_64_t)(0x0000000000800000) + +// Special handling: skull in flight. +// Neither a cacodemon nor a missile. +#define MF_SKULLFLY (uint_64_t)(0x0000000001000000) + +// Don't spawn this object +// in death match mode (e.g. key cards). +#define MF_NOTDMATCH (uint_64_t)(0x0000000002000000) + +// 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 +#define MF_TRANSLATION (uint_64_t)(0x000000000c000000) +#define MF_TRANSLATION1 (uint_64_t)(0x0000000004000000) +#define MF_TRANSLATION2 (uint_64_t)(0x0000000008000000) +// Hmm ???. +#define MF_TRANSSHIFT 26 + +#define MF_UNUSED2 (uint_64_t)(0x0000000010000000) +#define MF_UNUSED3 (uint_64_t)(0x0000000020000000) + + // Translucent sprite? // phares +#define MF_TRANSLUCENT (uint_64_t)(0x0000000040000000) + +// this is free LONGLONG(0x0000000100000000) + +// these are greater than an int. That's why the flags below are now uint_64_t + +#define MF_TOUCHY LONGLONG(0x0000000100000000) +#define MF_BOUNCES LONGLONG(0x0000000200000000) +#define MF_FRIEND LONGLONG(0x0000000400000000) + +#define MF_RESSURECTED LONGLONG(0x0000001000000000) +#define MF_NO_DEPTH_TEST LONGLONG(0x0000002000000000) +#define MF_FOREGROUND LONGLONG(0x0000004000000000) +#define MF_PLAYERSPRITE LONGLONG(0x0000008000000000) + +// This actor not targetted when it hurts something else +#define MF_NOTARGET LONGLONG(0x0000010000000000) +// fly mode is active +#define MF_FLY LONGLONG(0x0000020000000000) +// [FG] colored blood and gibs +#define MF_COLOREDBLOOD LONGLONG(0x0000040000000000) + +#define ALIVE(thing) ((thing->health > 0) && ((thing->flags & (MF_COUNTKILL | MF_CORPSE | MF_RESSURECTED)) == MF_COUNTKILL)) + +// killough 9/15/98: Same, but internal flags, not intended for .deh +// (some degree of opaqueness is good, to avoid compatibility woes) + +enum { + MIF_FALLING = 1, // Object is falling + MIF_ARMED = 2, // Object is armed (for MF_TOUCHY objects) +}; + +// Map Object definition. +// +// +// killough 2/20/98: +// +// WARNING: Special steps must be taken in p_saveg.c if C pointers are added to +// this mobj_s struct, or else savegames will crash when loaded. See p_saveg.c. +// Do not add "struct mobj_s *fooptr" without adding code to p_saveg.c to +// convert the pointers to ordinals and back for savegames. This was the whole +// reason behind monsters going to sleep when loading savegames (the "target" +// pointer was simply nullified after loading, to prevent Doom from crashing), +// and the whole reason behind loadgames crashing on savegames of AV attacks. +// + +// killough 9/8/98: changed some fields to shorts, +// for better memory usage (if only for cache). +/* cph 2006/08/28 - move Prev[XYZ] fields to the end of the struct. Add any + * other new fields to the end, and make sure you don't break savegames! */ + +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; // killough 8/10/98: change to ptr-to-ptr + + //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; // killough 8/11/98: change to ptr-to-ptr + + struct subsector_s* subsector; + + // The closest interval over all contacted Sectors. + fixed_t floorz; + fixed_t ceilingz; + + // killough 11/98: the lowest floor over all contacted Sectors. + fixed_t dropoffz; + + // 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; + uint_64_t flags; + int intflags; // killough 9/15/98: internal flags + int health; + + // Movement direction, movement generation (zig-zagging). + short movedir; // 0-7 + short movecount; // when 0, select a new dir + short strafecount; // killough 9/8/98: monster strafing + + // 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. + short reactiontime; + + // If >0, the current target will be chased no + // matter what (even if shot by another object) + short threshold; + + // killough 9/9/98: How long a monster pursues a target. + short pursuecount; + + short gear; // killough 11/98: used in torque simulation + + // Additional info record for player avatars only. + // Only valid if type == MT_PLAYER + struct player_s* player; + + // Player number last looked for. + short lastlook; + + // For nightmare respawn. + mapthing_t spawnpoint; + + // Thing being chased/attacked for tracers. + struct mobj_s* tracer; + + // new field: last known enemy -- killough 2/15/98 + struct mobj_s* lastenemy; + + // killough 8/2/98: friction properties part of sectors, + // not objects -- removed friction properties from here + // e6y: restored friction properties here + // Friction values for the sector the object is in + int friction; // phares 3/17/98 + int movefactor; + + // a linked list of sectors where this object appears + struct msecnode_s* touching_sectorlist; // phares 3/14/98 + + fixed_t PrevX; + fixed_t PrevY; + fixed_t PrevZ; + + //e6y + angle_t pitch; // orientation + int index; + short patch_width; + + int iden_nums; // hi word stores thing num, low word identifier num + + fixed_t bloodcolor; // [FG] renamed from "pad", now used to track the thing's blood color + + // SEE WARNING ABOVE ABOUT POINTER FIELDS!!! +} mobj_t; + +// External declarations (fomerly in p_local.h) -- killough 5/2/98 + +#define VIEWHEIGHT (41*FRACUNIT) + +#define GRAVITY FRACUNIT +#define MAXMOVE (30*FRACUNIT) + +#define ONFLOORZ INT_MIN +#define ONCEILINGZ INT_MAX + +// Time interval for item respawning. +#define ITEMQUESIZE 128 + +#define FLOATSPEED (FRACUNIT*4) +#define STOPSPEED (FRACUNIT/16) + +// killough 11/98: +// For torque simulation: + +#define OVERDRIVE 6 +#define MAXGEAR (OVERDRIVE+16) + +// killough 11/98: +// Whether an object is "sentient" or not. Used for environmental influences. +#define sentient(mobj) ((mobj)->health > 0 && (mobj)->info->seestate) + +extern int iquehead; +extern int iquetail; + +// [FG] colored blood and gibs +extern dboolean colored_blood; + +mobj_t* P_SubstNullMobj (mobj_t* th); +void P_RespawnSpecials(void); +mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); +void P_RemoveMobj(mobj_t *th); +dboolean P_SetMobjState(mobj_t *mobj, statenum_t state); +void P_MobjThinker(mobj_t *mobj); +void P_SpawnPuff(fixed_t x, fixed_t y, fixed_t z); +void P_SpawnBlood(fixed_t x, fixed_t y, fixed_t z, int damage, mobj_t* bleeder); +mobj_t *P_SpawnMissile(mobj_t *source, mobj_t *dest, mobjtype_t type); +void P_SpawnPlayerMissile(mobj_t *source, mobjtype_t type); +dboolean P_IsDoomnumAllowed(int doomnum); +mobj_t* P_SpawnMapThing (const mapthing_t* mthing, int index); +void P_SpawnPlayer(int n, const mapthing_t *mthing); +void P_CheckMissileSpawn(mobj_t*); // killough 8/2/98 +void P_ExplodeMissile(mobj_t*); // killough +#endif + diff --git a/src/p_plats.c b/src/p_plats.c new file mode 100644 index 0000000..ee9f193 --- /dev/null +++ b/src/p_plats.c @@ -0,0 +1,454 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Plats (i.e. elevator platforms) code, raising/lowering. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "m_random.h" +#include "r_main.h" +#include "p_spec.h" +#include "p_tick.h" +#include "s_sound.h" +#include "sounds.h" +#include "lprintf.h" +#include "e6y.h"//e6y + +platlist_t *activeplats; // killough 2/14/98: made global again + +// +// T_PlatRaise() +// +// Action routine to move a plat up and down +// +// Passed a plat structure containing all pertinent information about the move +// No return +// +// jff 02/08/98 all cases with labels beginning with gen added to support +// generalized line type behaviors. + +void T_PlatRaise(plat_t* plat) +{ + result_e res; + + // handle plat moving, up, down, waiting, or in stasis, + switch(plat->status) + { + case up: // plat moving up + res = T_MovePlane(plat->sector,plat->speed,plat->high,plat->crush,0,1); + + // if a pure raise type, make the plat moving sound + if (plat->type == raiseAndChange + || plat->type == raiseToNearestAndChange) + { + if (!(leveltime&7)) + S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_stnmov); + } + + // if encountered an obstacle, and not a crush type, reverse direction + if (res == crushed && (!plat->crush)) + { + plat->count = plat->wait; + plat->status = down; + S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_pstart); + + if (demo_compatibility && + (plat->type == raiseToNearestAndChange || + plat->type == raiseAndChange)) + { + // For these types vanilla did not initialize plat->low in EV_DoPlat, + // so they may descend to any depth, or not at all. + // See https://sourceforge.net/p/prboom-plus/bugs/211/ . + lprintf(LO_WARN, "T_PlatRaise: raise-and-change type has reversed " + "direction in compatibility mode - may lead to desync\n" + " gametic: %d sector: %d complevel: %d\n", + gametic, plat->sector->iSectorID, compatibility_level); + } + } + else // else handle reaching end of up stroke + { + if (res == pastdest) // end of stroke + { + // if not an instant toggle type, wait, make plat stop sound + if (plat->type!=toggleUpDn) + { + plat->count = plat->wait; + plat->status = waiting; + S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_pstop); + } + else // else go into stasis awaiting next toggle activation + { + plat->oldstatus = plat->status;//jff 3/14/98 after action wait + plat->status = in_stasis; //for reactivation of toggle + } + + // lift types and pure raise types are done at end of up stroke + // only the perpetual type waits then goes back up + switch(plat->type) + { + case blazeDWUS: + case downWaitUpStay: + case raiseAndChange: + case raiseToNearestAndChange: + case genLift: + P_RemoveActivePlat(plat); // killough + default: + break; + } + } + } + break; + + case down: // plat moving down + res = T_MovePlane(plat->sector,plat->speed,plat->low,false,0,-1); + + // handle reaching end of down stroke + if (res == pastdest) + { + // if not an instant toggle, start waiting, make plat stop sound + if (plat->type!=toggleUpDn) //jff 3/14/98 toggle up down + { // is silent, instant, no waiting + plat->count = plat->wait; + plat->status = waiting; + S_StartSound((mobj_t *)&plat->sector->soundorg,sfx_pstop); + } + else // instant toggles go into stasis awaiting next activation + { + plat->oldstatus = plat->status;//jff 3/14/98 after action wait + plat->status = in_stasis; //for reactivation of toggle + } + + //jff 1/26/98 remove the plat if it bounced so it can be tried again + //only affects plats that raise and bounce + //killough 1/31/98: relax compatibility to demo_compatibility + + // remove the plat if its a pure raise type + if (!comp[comp_floors]) + { + switch(plat->type) + { + case raiseAndChange: + case raiseToNearestAndChange: + P_RemoveActivePlat(plat); + default: + break; + } + } + } + break; + + case waiting: // plat is waiting + if (!--plat->count) // downcount and check for delay elapsed + { + if (plat->sector->floorheight == plat->low) + plat->status = up; // if at bottom, start up + else + plat->status = down; // if at top, start down + + // make plat start sound + S_StartSound((mobj_t *)&plat->sector->soundorg,sfx_pstart); + } + break; //jff 1/27/98 don't pickup code added later to in_stasis + + case in_stasis: // do nothing if in stasis + break; + } +} + + +// +// EV_DoPlat +// +// Handle Plat linedef types +// +// Passed the linedef that activated the plat, the type of plat action, +// and for some plat types, an amount to raise +// Returns true if a thinker is started, or restarted from stasis +// +int EV_DoPlat +( line_t* line, + plattype_e type, + int amount ) +{ + plat_t* plat; + int secnum; + int rtn; + sector_t* sec; + + secnum = -1; + rtn = 0; + + if (ProcessNoTagLines(line, &sec, &secnum)) {if (zerotag_manual) goto manual_plat; else {return rtn;}};//e6y + // Activate all plats that are in_stasis + switch(type) + { + case perpetualRaise: + P_ActivateInStasis(line->tag); + break; + + case toggleUpDn: + P_ActivateInStasis(line->tag); + rtn=1; + break; + + default: + break; + } + + // act on all sectors tagged the same as the activating linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_plat://e6y + // don't start a second floor function if already moving + if (P_SectorActive(floor_special,sec)) {//jff 2/23/98 multiple thinkers + if (!zerotag_manual) continue; else {return rtn;}};//e6y + + // Create a thinker + rtn = 1; + plat = Z_Malloc( sizeof(*plat), PU_LEVSPEC, 0); + memset(plat, 0, sizeof(*plat)); + P_AddThinker(&plat->thinker); + + plat->type = type; + plat->sector = sec; + plat->sector->floordata = plat; //jff 2/23/98 multiple thinkers + plat->thinker.function = T_PlatRaise; + plat->crush = false; + plat->tag = line->tag; + + //jff 1/26/98 Avoid raise plat bouncing a head off a ceiling and then + //going down forever -- default low to plat height when triggered + plat->low = sec->floorheight; + + // set up plat according to type + switch(type) + { + case raiseToNearestAndChange: + plat->speed = PLATSPEED/2; + sec->floorpic = sides[line->sidenum[0]].sector->floorpic; + plat->high = P_FindNextHighestFloor(sec,sec->floorheight); + plat->wait = 0; + plat->status = up; + sec->special = 0; + //jff 3/14/98 clear old field as well + sec->oldspecial = 0; + + S_StartSound((mobj_t *)&sec->soundorg,sfx_stnmov); + break; + + case raiseAndChange: + plat->speed = PLATSPEED/2; + sec->floorpic = sides[line->sidenum[0]].sector->floorpic; + plat->high = sec->floorheight + amount*FRACUNIT; + plat->wait = 0; + plat->status = up; + + S_StartSound((mobj_t *)&sec->soundorg,sfx_stnmov); + break; + + case downWaitUpStay: + plat->speed = PLATSPEED * 4; + plat->low = P_FindLowestFloorSurrounding(sec); + + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + + plat->high = sec->floorheight; + plat->wait = 35*PLATWAIT; + plat->status = down; + S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart); + break; + + case blazeDWUS: + plat->speed = PLATSPEED * 8; + plat->low = P_FindLowestFloorSurrounding(sec); + + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + + plat->high = sec->floorheight; + plat->wait = 35*PLATWAIT; + plat->status = down; + S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart); + break; + + case perpetualRaise: + plat->speed = PLATSPEED; + plat->low = P_FindLowestFloorSurrounding(sec); + + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + + plat->high = P_FindHighestFloorSurrounding(sec); + + if (plat->high < sec->floorheight) + plat->high = sec->floorheight; + + plat->wait = 35*PLATWAIT; + plat->status = P_Random(pr_plats)&1; + + S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart); + break; + + case toggleUpDn: //jff 3/14/98 add new type to support instant toggle + plat->speed = PLATSPEED; //not used + plat->wait = 35*PLATWAIT; //not used + plat->crush = true; //jff 3/14/98 crush anything in the way + + // set up toggling between ceiling, floor inclusive + plat->low = sec->ceilingheight; + plat->high = sec->floorheight; + plat->status = down; + break; + + default: + break; + } + P_AddActivePlat(plat); // add plat to list of active plats + if (zerotag_manual) return rtn; //e6y + } + return rtn; +} + +// The following were all rewritten by Lee Killough +// to use the new structure which places no limits +// on active plats. It also avoids spending as much +// time searching for active plats. Previously a +// fixed-size array was used, with NULL indicating +// empty entries, while now a doubly-linked list +// is used. + +// +// P_ActivateInStasis() +// +// Activate a plat that has been put in stasis +// (stopped perpetual floor, instant floor/ceil toggle) +// +// Passed the tag of the plat that should be reactivated +// Returns nothing +// +void P_ActivateInStasis(int tag) +{ + platlist_t *pl; + for (pl=activeplats; pl; pl=pl->next) // search the active plats + { + plat_t *plat = pl->plat; // for one in stasis with right tag + if (plat->tag == tag && plat->status == in_stasis) + { + if (plat->type==toggleUpDn) //jff 3/14/98 reactivate toggle type + plat->status = plat->oldstatus==up? down : up; + else + plat->status = plat->oldstatus; + plat->thinker.function = T_PlatRaise; + } + } +} + +// +// EV_StopPlat() +// +// Handler for "stop perpetual floor" linedef type +// +// Passed the linedef that stopped the plat +// Returns true if a plat was put in stasis +// +// jff 2/12/98 added int return value, fixed return +// +int EV_StopPlat(line_t* line) +{ + platlist_t *pl; + for (pl=activeplats; pl; pl=pl->next) // search the active plats + { + plat_t *plat = pl->plat; // for one with the tag not in stasis + if (plat->status != in_stasis && plat->tag == line->tag) + { + plat->oldstatus = plat->status; // put it in stasis + plat->status = in_stasis; + plat->thinker.function = NULL; + } + } + return 1; +} + +// +// P_AddActivePlat() +// +// Add a plat to the head of the active plat list +// +// Passed a pointer to the plat to add +// Returns nothing +// +void P_AddActivePlat(plat_t* plat) +{ + platlist_t *list = malloc(sizeof *list); + list->plat = plat; + plat->list = list; + if ((list->next = activeplats)) + list->next->prev = &list->next; + list->prev = &activeplats; + activeplats = list; +} + +// +// P_RemoveActivePlat() +// +// Remove a plat from the active plat list +// +// Passed a pointer to the plat to remove +// Returns nothing +// +void P_RemoveActivePlat(plat_t* plat) +{ + platlist_t *list = plat->list; + plat->sector->floordata = NULL; //jff 2/23/98 multiple thinkers + P_RemoveThinker(&plat->thinker); + if ((*list->prev = list->next)) + list->next->prev = list->prev; + free(list); +} + +// +// P_RemoveAllActivePlats() +// +// Remove all plats from the active plat list +// +// Passed nothing, returns nothing +// +void P_RemoveAllActivePlats(void) +{ + while (activeplats) + { + platlist_t *next = activeplats->next; + free(activeplats); + activeplats = next; + } +} diff --git a/src/p_pspr.c b/src/p_pspr.c new file mode 100644 index 0000000..8657216 --- /dev/null +++ b/src/p_pspr.c @@ -0,0 +1,974 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Weapon sprite animation, weapon objects. + * Action functions for weapons. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "r_main.h" +#include "p_map.h" +#include "p_inter.h" +#include "p_pspr.h" +#include "p_enemy.h" +#include "p_tick.h" +#include "m_random.h" +#include "s_sound.h" +#include "sounds.h" +#include "d_event.h" +#include "r_demo.h" +#include "g_game.h" +#include "lprintf.h" +#include "e6y.h"//e6y + +#define LOWERSPEED (FRACUNIT*6) +#define RAISESPEED (FRACUNIT*6) +#define WEAPONBOTTOM (FRACUNIT*128) +#define WEAPONTOP (FRACUNIT*32) + +#define BFGCELLS bfgcells /* Ty 03/09/98 externalized in p_inter.c */ + +// Checking correctness of input parameters for weapon codepointers +// for avoiding crashes when they are used with player/monster states. +#ifdef PRBOOM_DEBUG + #define CHECK_WEAPON_CODEPOINTER(codepointer, player)\ + if (!player->mo->player) {\ + I_Error("%s: Weapon codepointers cannot be used with player/monster states (incorrect DEH).", codepointer);\ + return;\ + } +#else + #define CHECK_WEAPON_CODEPOINTER(codepointer, player) +#endif + +extern void P_Thrust(player_t *, angle_t, fixed_t); + +// The following array holds the recoil values // phares + +static const int recoil_values[] = { // phares + 10, // wp_fist + 10, // wp_pistol + 30, // wp_shotgun + 10, // wp_chaingun + 100,// wp_missile + 20, // wp_plasma + 100,// wp_bfg + 0, // wp_chainsaw + 80 // wp_supershotgun +}; + +// +// P_SetPsprite +// + +static void P_SetPsprite(player_t *player, int position, statenum_t stnum) +{ + pspdef_t *psp = &player->psprites[position]; + + do + { + state_t *state; + + if (!stnum) + { + // object removed itself + psp->state = NULL; + break; + } + + state = &states[stnum]; + psp->state = state; + psp->tics = state->tics; // could be 0 + + if (state->misc1) + { + // coordinate set + psp->sx = state->misc1 << FRACBITS; + psp->sy = state->misc2 << FRACBITS; + } + + // Call action routine. + // Modified handling. + if (state->action) + { + state->action(player, psp); + if (!psp->state) + break; + } + stnum = psp->state->nextstate; + } + while (!psp->tics); // an initial state of 0 could cycle through +} + +// +// P_BringUpWeapon +// Starts bringing the pending weapon up +// from the bottom of the screen. +// Uses player +// + +static void P_BringUpWeapon(player_t *player) +{ + statenum_t newstate; + + if (player->pendingweapon == wp_nochange) + player->pendingweapon = player->readyweapon; + + if (player->pendingweapon == wp_chainsaw) + S_StartSound (player->mo, sfx_sawup); + + if (player->pendingweapon >= NUMWEAPONS) + lprintf(LO_WARN, "P_BringUpWeapon: weaponinfo overrun has occurred.\n"); + + newstate = weaponinfo[player->pendingweapon].upstate; + + player->pendingweapon = wp_nochange; + // killough 12/98: prevent pistol from starting visibly at bottom of screen: + player->psprites[ps_weapon].sy = + mbf_features ? WEAPONBOTTOM+FRACUNIT*2 : WEAPONBOTTOM; + + P_SetPsprite(player, ps_weapon, newstate); +} + +// The first set is where the weapon preferences from // killough, +// default.cfg are stored. These values represent the keys used // phares +// in DOOM2 to bring up the weapon, i.e. 6 = plasma gun. These // | +// are NOT the wp_* constants. // V + +int weapon_preferences[2][NUMWEAPONS+1] = { + {6, 9, 4, 3, 2, 8, 5, 7, 1, 0}, // !compatibility preferences + {6, 9, 4, 3, 2, 8, 5, 7, 1, 0}, // compatibility preferences +}; + +// Center Weapon when Firing. +int weapon_attack_alignment=0; + +// P_SwitchWeapon checks current ammo levels and gives you the +// most preferred weapon with ammo. It will not pick the currently +// raised weapon. When called from P_CheckAmmo this won't matter, +// because the raised weapon has no ammo anyway. When called from +// G_BuildTiccmd you want to toggle to a different weapon regardless. + +int P_SwitchWeapon(player_t *player) +{ + int *prefer = weapon_preferences[demo_compatibility!=0]; // killough 3/22/98 + int currentweapon = player->readyweapon; + int newweapon = currentweapon; + int i = NUMWEAPONS+1; // killough 5/2/98 + + // killough 2/8/98: follow preferences and fix BFG/SSG bugs + + do + switch (*prefer++) + { + case 1: + if (!player->powers[pw_strength]) // allow chainsaw override + break; + // fallthrough + case 0: + newweapon = wp_fist; + break; + case 2: + if (player->ammo[am_clip]) + newweapon = wp_pistol; + break; + case 3: + if (player->weaponowned[wp_shotgun] && player->ammo[am_shell]) + newweapon = wp_shotgun; + break; + case 4: + if (player->weaponowned[wp_chaingun] && player->ammo[am_clip]) + newweapon = wp_chaingun; + break; + case 5: + if (player->weaponowned[wp_missile] && player->ammo[am_misl]) + newweapon = wp_missile; + break; + case 6: + if (player->weaponowned[wp_plasma] && player->ammo[am_cell] && + gamemode != shareware) + newweapon = wp_plasma; + break; + case 7: + if (player->weaponowned[wp_bfg] && gamemode != shareware && + player->ammo[am_cell] >= (demo_compatibility ? 41 : 40)) + newweapon = wp_bfg; + break; + case 8: + if (player->weaponowned[wp_chainsaw]) + newweapon = wp_chainsaw; + break; + case 9: + if (player->weaponowned[wp_supershotgun] && gamemode == commercial && + player->ammo[am_shell] >= (demo_compatibility ? 3 : 2)) + newweapon = wp_supershotgun; + break; + } + while (newweapon==currentweapon && --i); // killough 5/2/98 + return newweapon; +} + +// killough 5/2/98: whether consoleplayer prefers weapon w1 over weapon w2. +int P_WeaponPreferred(int w1, int w2) +{ + return + (weapon_preferences[0][0] != ++w2 && (weapon_preferences[0][0] == ++w1 || + (weapon_preferences[0][1] != w2 && (weapon_preferences[0][1] == w1 || + (weapon_preferences[0][2] != w2 && (weapon_preferences[0][2] == w1 || + (weapon_preferences[0][3] != w2 && (weapon_preferences[0][3] == w1 || + (weapon_preferences[0][4] != w2 && (weapon_preferences[0][4] == w1 || + (weapon_preferences[0][5] != w2 && (weapon_preferences[0][5] == w1 || + (weapon_preferences[0][6] != w2 && (weapon_preferences[0][6] == w1 || + (weapon_preferences[0][7] != w2 && (weapon_preferences[0][7] == w1 + )))))))))))))))); +} + +// +// P_CheckAmmo +// Returns true if there is enough ammo to shoot. +// If not, selects the next weapon to use. +// (only in demo_compatibility mode -- killough 3/22/98) +// + +dboolean P_CheckAmmo(player_t *player) +{ + ammotype_t ammo = weaponinfo[player->readyweapon].ammo; + int count = 1; // Regular + + if (player->readyweapon == wp_bfg) // Minimal amount for one shot varies. + count = BFGCELLS; + else + if (player->readyweapon == wp_supershotgun) // Double barrel. + count = 2; + + // 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. + // + // killough 3/22/98: for old demos we do the switch here and now; + // for Boom games we cannot do this, and have different player + // preferences across demos or networks, so we have to use the + // G_BuildTiccmd() interface instead of making the switch here. + + if (demo_compatibility) + { + player->pendingweapon = P_SwitchWeapon(player); // phares + // Now set appropriate weapon overlay. + P_SetPsprite(player,ps_weapon,weaponinfo[player->readyweapon].downstate); + } + + return false; +} + +// +// P_FireWeapon. +// + +static void P_FireWeapon(player_t *player) +{ + statenum_t newstate; + + if (!P_CheckAmmo(player)) + return; + + P_SetMobjState(player->mo, S_PLAY_ATK1); + newstate = weaponinfo[player->readyweapon].atkstate; + P_SetPsprite(player, ps_weapon, newstate); + P_NoiseAlert(player->mo, player->mo); +} + +// +// P_DropWeapon +// Player died, so put the weapon away. +// + +void P_DropWeapon(player_t *player) +{ + P_SetPsprite(player, ps_weapon, weaponinfo[player->readyweapon].downstate); +} + +// +// A_WeaponReady +// The player can fire the weapon +// or change to another weapon at this time. +// Follows after getting weapon up, +// or after previous attack/fire sequence. +// + +void A_WeaponReady(player_t *player, pspdef_t *psp) +{ + CHECK_WEAPON_CODEPOINTER("A_WeaponReady", player); + +// weapon change sequence considered complete + done_autoswitch = false; + + // get out of attack state + if (player->mo->state == &states[S_PLAY_ATK1] + || player->mo->state == &states[S_PLAY_ATK2] ) + P_SetMobjState(player->mo, S_PLAY); + + if (player->readyweapon == wp_chainsaw && psp->state == &states[S_SAW]) + S_StartSound(player->mo, sfx_sawidl); + + // check for change + // if player is dead, put the weapon away + + if (player->pendingweapon != wp_nochange || !player->health) + { + // change weapon (pending weapon should already be validated) + statenum_t newstate = weaponinfo[player->readyweapon].downstate; + P_SetPsprite(player, ps_weapon, newstate); + return; + } + + // check for fire + // the missile launcher and bfg do not auto fire + + if (player->cmd.buttons & BT_ATTACK) + { + if (!player->attackdown || (player->readyweapon != wp_missile && + player->readyweapon != wp_bfg)) + { + player->attackdown = true; + P_FireWeapon(player); + return; + } + } + else + player->attackdown = false; + + // bob the weapon based on movement speed + { + int angle = (128*leveltime) & FINEMASK; + psp->sx = FRACUNIT + FixedMul(player->bob, finecosine[angle]); + angle &= FINEANGLES/2-1; + psp->sy = WEAPONTOP + FixedMul(player->bob, finesine[angle]); + } +} + +// +// A_ReFire +// The player can re-fire the weapon +// without lowering it entirely. +// + +void A_ReFire(player_t *player, pspdef_t *psp) +{ + CHECK_WEAPON_CODEPOINTER("A_ReFire", player); + + // check for fire + // (if a weaponchange is pending, let it go through instead) + + if ( (player->cmd.buttons & BT_ATTACK) + && player->pendingweapon == wp_nochange && player->health) + { + player->refire++; + P_FireWeapon(player); + } + else + { + player->refire = 0; + P_CheckAmmo(player); + } +} + +void A_CheckReload(player_t *player, pspdef_t *psp) +{ + CHECK_WEAPON_CODEPOINTER("A_CheckReload", player); + + if (!P_CheckAmmo(player) && compatibility_level >= prboom_4_compatibility) { + /* cph 2002/08/08 - In old Doom, P_CheckAmmo would start the weapon lowering + * immediately. This was lost in Boom when the weapon switching logic was + * rewritten. But we must tell Doom that we don't need to complete the + * reload frames for the weapon here. G_BuildTiccmd will set ->pendingweapon + * for us later on. */ + P_SetPsprite(player,ps_weapon,weaponinfo[player->readyweapon].downstate); + } +} + +// +// A_Lower +// Lowers current weapon, +// and changes weapon at bottom. +// + +void A_Lower(player_t *player, pspdef_t *psp) +{ + CHECK_WEAPON_CODEPOINTER("A_Lower", player); + + psp->sy += LOWERSPEED; + + // Is already down. + if (psp->sy < WEAPONBOTTOM) + return; + + // Player is dead. + if (player->playerstate == PST_DEAD) + { + psp->sy = WEAPONBOTTOM; + return; // don't bring weapon back up + } + + // The old weapon has been lowered off the screen, + // so change the weapon and start raising it + + if (!player->health) + { // Player is dead, so keep the weapon off screen. + P_SetPsprite(player, ps_weapon, S_NULL); + return; + } + + // haleyjd 03/28/10: do not assume pendingweapon is valid + // e6y: probably for future complevels + // if(player->pendingweapon < NUMWEAPONS) + player->readyweapon = player->pendingweapon; + + P_BringUpWeapon(player); +} + +// +// A_Raise +// + +void A_Raise(player_t *player, pspdef_t *psp) +{ + statenum_t newstate; + + CHECK_WEAPON_CODEPOINTER("A_Raise", player); + + psp->sy -= RAISESPEED; + + if (psp->sy > WEAPONTOP) + return; + + psp->sy = WEAPONTOP; + + // The weapon has been raised all the way, + // so change to the ready state. + + newstate = weaponinfo[player->readyweapon].readystate; + + P_SetPsprite(player, ps_weapon, newstate); +} + + +// Weapons now recoil, amount depending on the weapon. // phares +// // | +// The P_SetPsprite call in each of the weapon firing routines // V +// was moved here so the recoil could be synched with the +// muzzle flash, rather than the pressing of the trigger. +// The BFG delay caused this to be necessary. + +static void A_FireSomething(player_t* player,int adder) +{ + P_SetPsprite(player, ps_flash, + weaponinfo[player->readyweapon].flashstate+adder); + + // killough 3/27/98: prevent recoil in no-clipping mode + if (!(player->mo->flags & MF_NOCLIP)) + if (!compatibility && weapon_recoil) + P_Thrust(player, + ANG180+player->mo->angle, // ^ + 2048*recoil_values[player->readyweapon]); // | +} // phares + +// +// A_GunFlash +// + +void A_GunFlash(player_t *player, pspdef_t *psp) +{ + CHECK_WEAPON_CODEPOINTER("A_GunFlash", player); + + P_SetMobjState(player->mo, S_PLAY_ATK2); + + A_FireSomething(player,0); // phares +} + +// +// WEAPON ATTACKS +// + +// +// A_Punch +// + +void A_Punch(player_t *player, pspdef_t *psp) +{ + angle_t angle; + int t, slope, damage; + + CHECK_WEAPON_CODEPOINTER("A_Punch", player); + + damage = (P_Random(pr_punch)%10+1)<<1; + + if (player->powers[pw_strength]) + damage *= 10; + + angle = player->mo->angle; + + // killough 5/5/98: remove dependence on order of evaluation: + t = P_Random(pr_punchangle); + angle += (t - P_Random(pr_punchangle))<<18; + + /* killough 8/2/98: make autoaiming prefer enemies */ + if (!mbf_features || + (slope = P_AimLineAttack(player->mo, angle, MELEERANGE, MF_FRIEND), + !linetarget)) + slope = P_AimLineAttack(player->mo, angle, MELEERANGE, 0); + + P_LineAttack(player->mo, angle, MELEERANGE, slope, damage); + + if (!linetarget) + return; + + S_StartSound(player->mo, sfx_punch); + + // turn to face target + + player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, + linetarget->x, linetarget->y); + R_SmoothPlaying_Reset(player); // e6y +} + +// +// A_Saw +// + +void A_Saw(player_t *player, pspdef_t *psp) +{ + int slope, damage; + angle_t angle; + int t; + + CHECK_WEAPON_CODEPOINTER("A_Saw", player); + + damage = 2*(P_Random(pr_saw)%10+1); + angle = player->mo->angle; + // killough 5/5/98: remove dependence on order of evaluation: + t = P_Random(pr_saw); + angle += (t - P_Random(pr_saw))<<18; + + /* Use meleerange + 1 so that the puff doesn't skip the flash + * killough 8/2/98: make autoaiming prefer enemies */ + if (!mbf_features || + (slope = P_AimLineAttack(player->mo, angle, MELEERANGE+1, MF_FRIEND), + !linetarget)) + slope = P_AimLineAttack(player->mo, angle, MELEERANGE+1, 0); + + P_LineAttack(player->mo, angle, MELEERANGE+1, slope, damage); + + if (!linetarget) + { + S_StartSound(player->mo, sfx_sawful); + return; + } + + S_StartSound(player->mo, sfx_sawhit); + + // turn to face target + angle = R_PointToAngle2(player->mo->x, player->mo->y, + linetarget->x, linetarget->y); + + if (angle - player->mo->angle > ANG180) { + if (angle - player->mo->angle < -ANG90/20) + player->mo->angle = angle + ANG90/21; + else + player->mo->angle -= ANG90/20; + } else { + if (angle - player->mo->angle > ANG90/20) + player->mo->angle = angle - ANG90/21; + else + player->mo->angle += ANG90/20; + } + + player->mo->flags |= MF_JUSTATTACKED; + R_SmoothPlaying_Reset(player); // e6y +} + +// +// A_FireMissile +// + +void A_FireMissile(player_t *player, pspdef_t *psp) +{ + CHECK_WEAPON_CODEPOINTER("A_FireMissile", player); + + player->ammo[weaponinfo[player->readyweapon].ammo]--; + P_SpawnPlayerMissile(player->mo, MT_ROCKET); +} + +// +// A_FireBFG +// + +void A_FireBFG(player_t *player, pspdef_t *psp) +{ + CHECK_WEAPON_CODEPOINTER("A_FireBFG", player); + + player->ammo[weaponinfo[player->readyweapon].ammo] -= BFGCELLS; + P_SpawnPlayerMissile(player->mo, MT_BFG); +} + +// +// A_FireOldBFG +// +// This function emulates Doom's Pre-Beta BFG +// By Lee Killough 6/6/98, 7/11/98, 7/19/98, 8/20/98 +// +// This code may not be used in other mods without appropriate credit given. +// Code leeches will be telefragged. + +int autoaim = 0; // killough 7/19/98: autoaiming was not in original beta +void A_FireOldBFG(player_t *player, pspdef_t *psp) +{ + int type = MT_PLASMA1; + + if (compatibility_level < mbf_compatibility) + return; + + CHECK_WEAPON_CODEPOINTER("A_FireOldBFG", player); + + if (weapon_recoil && !(player->mo->flags & MF_NOCLIP)) + P_Thrust(player, ANG180 + player->mo->angle, + 512*recoil_values[wp_plasma]); + + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + player->extralight = 2; + + do + { + mobj_t *th, *mo = player->mo; + angle_t an = mo->angle; + angle_t an1 = ((P_Random(pr_bfg)&127) - 64) * (ANG90/768) + an; + angle_t an2 = ((P_Random(pr_bfg)&127) - 64) * (ANG90/640) + ANG90; + extern int autoaim; + + if (autoaim/* || !beta_emulation*/) + { + // killough 8/2/98: make autoaiming prefer enemies + uint_64_t mask = mbf_features ? MF_FRIEND : 0; + fixed_t slope; + do + { + slope = P_AimLineAttack(mo, an, 16*64*FRACUNIT, mask); + if (!linetarget) + slope = P_AimLineAttack(mo, an += 1<<26, 16*64*FRACUNIT, mask); + if (!linetarget) + slope = P_AimLineAttack(mo, an -= 2<<26, 16*64*FRACUNIT, mask); + if (!linetarget) + slope = 0, an = mo->angle; + } + while (mask && (mask=0, !linetarget)); // killough 8/2/98 + an1 += an - mo->angle; + an2 += tantoangle[slope >> DBITS]; + } + + th = P_SpawnMobj(mo->x, mo->y, mo->z + 62*FRACUNIT - player->psprites[ps_weapon].sy, type); + P_SetTarget(&th->target, mo); + th->angle = an1; + th->momx = finecosine[an1>>ANGLETOFINESHIFT] * 25; + th->momy = finesine[an1>>ANGLETOFINESHIFT] * 25; + th->momz = finetangent[an2>>ANGLETOFINESHIFT] * 25; + P_CheckMissileSpawn(th); + } + while ((type != MT_PLASMA2) && (type = MT_PLASMA2)); //killough: obfuscated! +} + +// +// A_FirePlasma +// + +void A_FirePlasma(player_t *player, pspdef_t *psp) +{ + CHECK_WEAPON_CODEPOINTER("A_FirePlasma", player); + + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + A_FireSomething(player,P_Random(pr_plasma)&1); // phares + P_SpawnPlayerMissile(player->mo, MT_PLASMA); +} + +// +// P_BulletSlope +// Sets a slope so a near miss is at aproximately +// the height of the intended target +// + +//e6y static +fixed_t bulletslope; + +static void P_BulletSlope(mobj_t *mo) +{ + angle_t an = mo->angle; // see which target is to be aimed at + + if (comperr(comperr_freeaim)) + bulletslope = finetangent[(ANG90 - mo->pitch) >> ANGLETOFINESHIFT]; + else + { + /* killough 8/2/98: make autoaiming prefer enemies */ + uint_64_t mask = mbf_features ? MF_FRIEND : 0; + + do + { + bulletslope = P_AimLineAttack(mo, an, 16*64*FRACUNIT, mask); + if (!linetarget) + bulletslope = P_AimLineAttack(mo, an += 1<<26, 16*64*FRACUNIT, mask); + if (!linetarget) + bulletslope = P_AimLineAttack(mo, an -= 2<<26, 16*64*FRACUNIT, mask); + } + while (mask && (mask=0, !linetarget)); /* killough 8/2/98 */ + } +} + +// +// P_GunShot +// + +static void P_GunShot(mobj_t *mo, dboolean accurate) +{ + int damage = 5*(P_Random(pr_gunshot)%3+1); + angle_t angle = mo->angle; + + if (!accurate) + { // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_misfire); + angle += (t - P_Random(pr_misfire))<<18; + } + + P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage); +} + +// +// A_FirePistol +// + +void A_FirePistol(player_t *player, pspdef_t *psp) +{ + CHECK_WEAPON_CODEPOINTER("A_FirePistol", player); + + S_StartSound(player->mo, sfx_pistol); + + P_SetMobjState(player->mo, S_PLAY_ATK2); + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + A_FireSomething(player,0); // phares + P_BulletSlope(player->mo); + P_GunShot(player->mo, !player->refire); +} + +// +// A_FireShotgun +// + +void A_FireShotgun(player_t *player, pspdef_t *psp) +{ + int i; + + CHECK_WEAPON_CODEPOINTER("A_FireShotgun", player); + + S_StartSound(player->mo, sfx_shotgn); + P_SetMobjState(player->mo, S_PLAY_ATK2); + + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + A_FireSomething(player,0); // phares + + P_BulletSlope(player->mo); + + for (i=0; i<7; i++) + P_GunShot(player->mo, false); +} + +// +// A_FireShotgun2 +// + +void A_FireShotgun2(player_t *player, pspdef_t *psp) +{ + int i; + + CHECK_WEAPON_CODEPOINTER("A_FireShotgun2", player); + + S_StartSound(player->mo, sfx_dshtgn); + P_SetMobjState(player->mo, S_PLAY_ATK2); + player->ammo[weaponinfo[player->readyweapon].ammo] -= 2; + + A_FireSomething(player,0); // phares + + P_BulletSlope(player->mo); + + for (i=0; i<20; i++) + { + int damage = 5*(P_Random(pr_shotgun)%3+1); + angle_t angle = player->mo->angle; + // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_shotgun); + angle += (t - P_Random(pr_shotgun))<<19; + t = P_Random(pr_shotgun); + P_LineAttack(player->mo, angle, MISSILERANGE, bulletslope + + ((t - P_Random(pr_shotgun))<<5), damage); + } +} + +// +// A_FireCGun +// + +void A_FireCGun(player_t *player, pspdef_t *psp) +{ + CHECK_WEAPON_CODEPOINTER("A_FireCGun", player); + + if (player->ammo[weaponinfo[player->readyweapon].ammo] || default_comp[comp_sound]) + S_StartSound(player->mo, sfx_pistol); + + if (!player->ammo[weaponinfo[player->readyweapon].ammo]) + return; + + P_SetMobjState(player->mo, S_PLAY_ATK2); + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + A_FireSomething(player,psp->state - &states[S_CHAIN1]); // phares + + P_BulletSlope(player->mo); + + P_GunShot(player->mo, !player->refire); +} + +void A_Light0(player_t *player, pspdef_t *psp) +{ + CHECK_WEAPON_CODEPOINTER("A_Light0", player); + + player->extralight = 0; +} + +void A_Light1 (player_t *player, pspdef_t *psp) +{ + CHECK_WEAPON_CODEPOINTER("A_Light1", player); + + player->extralight = 1; +} + +void A_Light2 (player_t *player, pspdef_t *psp) +{ + CHECK_WEAPON_CODEPOINTER("A_Light2", player); + + player->extralight = 2; +} + +// +// A_BFGSpray +// Spawn a BFG explosion on every monster in view +// + +void A_BFGSpray(mobj_t *mo) +{ + int i; + + for (i=0 ; i<40 ; i++) // offset angles from its attack angle + { + int j, damage; + angle_t an = mo->angle - ANG90/2 + ANG90/40*i; + + // mo->target is the originator (player) of the missile + + // killough 8/2/98: make autoaiming prefer enemies + if (!mbf_features || + (P_AimLineAttack(mo->target, an, 16*64*FRACUNIT, MF_FRIEND), + !linetarget)) + P_AimLineAttack(mo->target, an, 16*64*FRACUNIT, 0); + + if (!linetarget) + continue; + + P_SpawnMobj(linetarget->x, linetarget->y, + linetarget->z + (linetarget->height>>2), MT_EXTRABFG); + + for (damage=j=0; j<15; j++) + damage += (P_Random(pr_bfg)&7) + 1; + + P_DamageMobj(linetarget, mo->target, mo->target, damage); + } +} + +// +// A_BFGsound +// + +void A_BFGsound(player_t *player, pspdef_t *psp) +{ + CHECK_WEAPON_CODEPOINTER("A_BFGsound", player); + + S_StartSound(player->mo, sfx_bfg); +} + +// +// P_SetupPsprites +// Called at start of level for each player. +// + +void P_SetupPsprites(player_t *player) +{ + int i; + + // remove all psprites + for (i=0; ipsprites[i].state = NULL; + + // spawn the gun + player->pendingweapon = player->readyweapon; + P_BringUpWeapon(player); +} + +// +// P_MovePsprites +// Called every tic by player thinking routine. +// + +void P_MovePsprites(player_t *player) +{ + pspdef_t *psp = player->psprites; + int i; + + // a null state means not active + // drop tic count and possibly change state + // a -1 tic count never changes + + for (i=0; istate && psp->tics != -1 && !--psp->tics) + P_SetPsprite(player, i, psp->state->nextstate); + + player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx; + player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy; +} diff --git a/src/p_pspr.h b/src/p_pspr.h new file mode 100644 index 0000000..f1fbad0 --- /dev/null +++ b/src/p_pspr.h @@ -0,0 +1,130 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Sprite animation. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_PSPR__ +#define __P_PSPR__ + +/* Basic data types. + * Needs fixed point, and BAM angles. */ + +#include "m_fixed.h" +#include "tables.h" + +/* Needs to include the precompiled sprite animation tables. + * + * Header generated by multigen utility. + * This includes all the data for thing animation, + * i.e. the Thing Atrributes table and the Frame Sequence table. + */ + +#include "info.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +/* + * Frame flags: + * handles maximum brightness (torches, muzzle flare, light sources) + */ + +#define FF_FULLBRIGHT 0x8000 /* flag in thing->frame */ +#define FF_FRAMEMASK 0x7fff + +/* + * Overlay psprites are scaled shapes + * drawn directly on the view screen, + * coordinates are given for a 320*200 view screen. + */ + +typedef enum +{ + ps_weapon, + ps_flash, + NUMPSPRITES +} psprnum_t; + +typedef struct +{ + state_t *state; /* a NULL state means not active */ + int tics; + fixed_t sx; + fixed_t sy; +} pspdef_t; + +enum +{ + CENTERWEAPON_OFF, + CENTERWEAPON_HOR, + CENTERWEAPON_HORVER, + CENTERWEAPON_BOB, + NUM_CENTERWEAPON, +}; + +extern int weapon_preferences[2][NUMWEAPONS+1]; /* killough 5/2/98 */ +extern int weapon_attack_alignment; +int P_WeaponPreferred(int w1, int w2); + +struct player_s; +int P_SwitchWeapon(struct player_s *player); +dboolean P_CheckAmmo(struct player_s *player); +void P_SetupPsprites(struct player_s *curplayer); +void P_MovePsprites(struct player_s *curplayer); +void P_DropWeapon(struct player_s *player); + +void A_Light0(); +void A_WeaponReady(); +void A_Lower(); +void A_Raise(); +void A_Punch(); +void A_ReFire(); +void A_FirePistol(); +void A_Light1(); +void A_FireShotgun(); +void A_Light2(); +void A_FireShotgun2(); +void A_CheckReload(); +void A_OpenShotgun2(); +void A_LoadShotgun2(); +void A_CloseShotgun2(); +void A_FireCGun(); +void A_GunFlash(); +void A_FireMissile(); +void A_Saw(); +void A_FirePlasma(); +void A_BFGsound(); +void A_FireBFG(); +void A_BFGSpray(); +void A_FireOldBFG(); + +#endif diff --git a/src/p_saveg.c b/src/p_saveg.c new file mode 100644 index 0000000..4d84bc1 --- /dev/null +++ b/src/p_saveg.c @@ -0,0 +1,1055 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Archiving: SaveGame I/O. + * + *-----------------------------------------------------------------------------*/ + +#include + +#include "doomstat.h" +#include "r_main.h" +#include "p_maputl.h" +#include "p_spec.h" +#include "p_tick.h" +#include "p_saveg.h" +#include "m_random.h" +#include "am_map.h" +#include "p_enemy.h" +#include "lprintf.h" +#include "s_advsound.h" +#include "e6y.h"//e6y + +byte *save_p; + +// Pads save_p to a 4-byte boundary +// so that the load/save works on SGI&Gecko. +#define PADSAVEP() do { save_p += (4 - ((intptr_t) save_p & 3)) & 3; } while (0) +// +// P_ArchivePlayers +// +void P_ArchivePlayers (void) +{ + int i; + + CheckSaveGame(sizeof(player_t) * MAXPLAYERS); // killough + for (i=0 ; ipsprites[j].state) + dest->psprites[j].state = + (state_t *)(dest->psprites[j].state-states); + } +} + +// +// P_UnArchivePlayers +// +void P_UnArchivePlayers (void) +{ + int i; + + for (i=0 ; ifloorheight + sizeof sec->ceilingheight) + * numsectors + sizeof(short)*3*numlines + 4 + 2; + + for (i=0; itextureoffset + sizeof si->rowoffset; + if (lines[i].sidenum[1] != NO_INDEX) + size += + sizeof(short)*3 + sizeof si->textureoffset + sizeof si->rowoffset; + } + + CheckSaveGame(size); // killough + + PADSAVEP(); // killough 3/22/98 + + put = (short *)save_p; + + // do sectors + for (i=0, sec = sectors ; ifloorheight, sizeof sec->floorheight); + put = (void *)((char *) put + sizeof sec->floorheight); + memcpy(put, &sec->ceilingheight, sizeof sec->ceilingheight); + put = (void *)((char *) put + sizeof sec->ceilingheight); + + *put++ = sec->floorpic; + *put++ = sec->ceilingpic; + *put++ = sec->lightlevel; + *put++ = sec->special; // needed? yes -- transfer types + *put++ = sec->tag; // needed? need them -- killough + } + + // do lines + for (i=0, li = lines ; iflags; + *put++ = li->special; + *put++ = li->tag; + + for (j=0; j<2; j++) + if (li->sidenum[j] != NO_INDEX) + { + si = &sides[li->sidenum[j]]; + + // killough 10/98: save full sidedef offsets, + // preserving fractional scroll offsets + + memcpy(put, &si->textureoffset, sizeof si->textureoffset); + put = (void *)((char *) put + sizeof si->textureoffset); + memcpy(put, &si->rowoffset, sizeof si->rowoffset); + put = (void *)((char *) put + sizeof si->rowoffset); + + *put++ = si->toptexture; + *put++ = si->bottomtexture; + *put++ = si->midtexture; + } + } + + *put++ = musinfo.current_item; + + save_p = (byte *) put; +} + + + +// +// P_UnArchiveWorld +// +void P_UnArchiveWorld (void) +{ + int i; + sector_t *sec; + line_t *li; + short *get; + + PADSAVEP(); // killough 3/22/98 + + get = (short *) save_p; + + // do sectors + for (i=0, sec = sectors ; ifloorheight, get, sizeof sec->floorheight); + get = (void *)((char *) get + sizeof sec->floorheight); + memcpy(&sec->ceilingheight, get, sizeof sec->ceilingheight); + get = (void *)((char *) get + sizeof sec->ceilingheight); + + sec->floorpic = *get++; + sec->ceilingpic = *get++; + sec->lightlevel = *get++; + sec->special = *get++; + sec->tag = *get++; + sec->ceilingdata = 0; //jff 2/22/98 now three thinker fields, not two + sec->floordata = 0; + sec->lightingdata = 0; + sec->soundtarget = 0; + } + + // do lines + for (i=0, li = lines ; iflags = *get++; + li->special = *get++; + li->tag = *get++; + for (j=0 ; j<2 ; j++) + if (li->sidenum[j] != NO_INDEX) + { + side_t *si = &sides[li->sidenum[j]]; + + // killough 10/98: load full sidedef offsets, including fractions + + memcpy(&si->textureoffset, get, sizeof si->textureoffset); + get = (void *)((char *) get + sizeof si->textureoffset); + memcpy(&si->rowoffset, get, sizeof si->rowoffset); + get = (void *)((char *) get + sizeof si->rowoffset); + + si->toptexture = *get++; + si->bottomtexture = *get++; + si->midtexture = *get++; + } + } + + musinfo.current_item = *get++; + + save_p = (byte *) get; +} + +// +// Thinkers +// + +typedef enum { + tc_end, + tc_mobj +} thinkerclass_t; + +// phares 9/13/98: Moved this code outside of P_ArchiveThinkers so the +// thinker indices could be used by the code that saves sector info. + +static int number_of_thinkers; + +void P_ThinkerToIndex(void) +{ + thinker_t *th; + + // killough 2/14/98: + // count the number of thinkers, and mark each one with its index, using + // the prev field as a placeholder, since it can be restored later. + + number_of_thinkers = 0; + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + if (th->function == P_MobjThinker) + th->prev = (thinker_t *)(intptr_t) ++number_of_thinkers; +} + +// phares 9/13/98: Moved this code outside of P_ArchiveThinkers so the +// thinker indices could be used by the code that saves sector info. + +void P_IndexToThinker(void) +{ + // killough 2/14/98: restore prev pointers + thinker_t *th; + thinker_t *prev = &thinkercap; + + for (th = thinkercap.next ; th != &thinkercap ; prev=th, th=th->next) + th->prev = prev; +} + +// +// P_ArchiveThinkers +// +// 2/14/98 killough: substantially modified to fix savegame bugs + +void P_ArchiveThinkers (void) +{ + thinker_t *th; + + CheckSaveGame(sizeof brain); // killough 3/26/98: Save boss brain state + memcpy(save_p, &brain, sizeof brain); + save_p += sizeof brain; + + /* check that enough room is available in savegame buffer + * - killough 2/14/98 + * cph - use number_of_thinkers saved by P_ThinkerToIndex above + * size per object is sizeof(mobj_t) - 2*sizeof(void*) - 4*sizeof(fixed_t) plus + * padded type (4) plus 5*sizeof(void*), i.e. sizeof(mobj_t) + 4 + + * 3*sizeof(void*) + * cph - +1 for the tc_end + */ + CheckSaveGame(number_of_thinkers*(sizeof(mobj_t)-3*sizeof(fixed_t)+4+3*sizeof(void*)) +1); + + // save off the current thinkers + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + if (th->function == P_MobjThinker) + { + mobj_t *mobj; + + *save_p++ = tc_mobj; + PADSAVEP(); + mobj = (mobj_t *)save_p; + + //e6y + memcpy (mobj, th, sizeof(*mobj)); + save_p += sizeof(*mobj); + + mobj->state = (state_t *)(mobj->state - states); + + // killough 2/14/98: convert pointers into indices. + // Fixes many savegame problems, by properly saving + // target and tracer fields. Note: we store NULL if + // the thinker pointed to by these fields is not a + // mobj thinker. + + if (mobj->target) + mobj->target = mobj->target->thinker.function == + P_MobjThinker ? + (mobj_t *) mobj->target->thinker.prev : NULL; + + if (mobj->tracer) + mobj->tracer = mobj->tracer->thinker.function == + P_MobjThinker ? + (mobj_t *) mobj->tracer->thinker.prev : NULL; + + // killough 2/14/98: new field: save last known enemy. Prevents + // monsters from going to sleep after killing monsters and not + // seeing player anymore. + + if (mobj->lastenemy) + mobj->lastenemy = mobj->lastenemy->thinker.function == + P_MobjThinker ? + (mobj_t *) mobj->lastenemy->thinker.prev : NULL; + + + // killough 2/14/98: end changes + + if (mobj->player) + mobj->player = (player_t *)((mobj->player-players) + 1); + } + + // add a terminating marker + *save_p++ = tc_end; + + // killough 9/14/98: save soundtargets + { + int i; + CheckSaveGame(numsectors * sizeof(mobj_t *)); // killough 9/14/98 + for (i = 0; i < numsectors; i++) + { + mobj_t *target = sectors[i].soundtarget; + // Fix crash on reload when a soundtarget points to a removed corpse + // (prboom bug #1590350) + if (target && target->thinker.function == P_MobjThinker) + target = (mobj_t *) target->thinker.prev; + else + target = NULL; + memcpy(save_p, &target, sizeof target); + save_p += sizeof target; + } + } +} + +/* + * killough 11/98 + * + * Same as P_SetTarget() in p_tick.c, except that the target is nullified + * first, so that no old target's reference count is decreased (when loading + * savegames, old targets are indices, not really pointers to targets). + */ + +static void P_SetNewTarget(mobj_t **mop, mobj_t *targ) +{ + *mop = NULL; + P_SetTarget(mop, targ); +} + +// +// P_UnArchiveThinkers +// +// 2/14/98 killough: substantially modified to fix savegame bugs +// + +// savegame file stores ints in the corresponding * field; this function +// safely casts them back to int. +static int P_GetMobj(mobj_t* mi, size_t s) +{ + size_t i = (size_t)mi; + if (i >= s) + I_Error("Corrupt savegame"); + return i; +} + +void P_UnArchiveThinkers (void) +{ + thinker_t *th; + mobj_t **mobj_p; // killough 2/14/98: Translation table + size_t size; // killough 2/14/98: size of or index into table + + totallive = 0; + // killough 3/26/98: Load boss brain state + memcpy(&brain, save_p, sizeof brain); + save_p += sizeof brain; + + // remove all the current thinkers + for (th = thinkercap.next; th != &thinkercap; ) + { + thinker_t *next = th->next; + if (th->function == P_MobjThinker) + { + P_RemoveMobj ((mobj_t *) th); + P_RemoveThinkerDelayed(th); // fix mobj leak + } + else + Z_Free (th); + th = next; + } + P_InitThinkers (); + + // killough 2/14/98: count number of thinkers by skipping through them + { + byte *sp = save_p; // save pointer and skip header + for (size = 1; *save_p++ == tc_mobj; size++) // killough 2/14/98 + { // skip all entries, adding up count + PADSAVEP(); + save_p += sizeof(mobj_t);//e6y + } + + if (*--save_p != tc_end) + I_Error ("P_UnArchiveThinkers: Unknown tclass %i in savegame", *save_p); + + // first table entry special: 0 maps to NULL + *(mobj_p = malloc(size * sizeof *mobj_p)) = 0; // table of pointers + save_p = sp; // restore save pointer + } + + // read in saved thinkers + for (size = 1; *save_p++ == tc_mobj; size++) // killough 2/14/98 + { + mobj_t *mobj = Z_Malloc(sizeof(mobj_t), PU_LEVEL, NULL); + + // killough 2/14/98 -- insert pointers to thinkers into table, in order: + mobj_p[size] = mobj; + + PADSAVEP(); + + memcpy (mobj, save_p, sizeof(mobj_t)); + save_p += sizeof(mobj_t); + + mobj->state = states + (intptr_t) mobj->state; + + if (mobj->player) + (mobj->player = &players[(size_t) mobj->player - 1]) -> mo = mobj; + + P_SetThingPosition (mobj); + mobj->info = &mobjinfo[mobj->type]; + + // killough 2/28/98: + // Fix for falling down into a wall after savegame loaded: + // mobj->floorz = mobj->subsector->sector->floorheight; + // mobj->ceilingz = mobj->subsector->sector->ceilingheight; + + mobj->thinker.function = P_MobjThinker; + P_AddThinker (&mobj->thinker); + + if (!((mobj->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL | MF_CORPSE))) + totallive++; + } + + // killough 2/14/98: adjust target and tracer fields, plus + // lastenemy field, to correctly point to mobj thinkers. + // NULL entries automatically handled by first table entry. + // + // killough 11/98: use P_SetNewTarget() to set fields + + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + { + P_SetNewTarget(&((mobj_t *) th)->target, + mobj_p[P_GetMobj(((mobj_t *)th)->target,size)]); + + P_SetNewTarget(&((mobj_t *) th)->tracer, + mobj_p[P_GetMobj(((mobj_t *)th)->tracer,size)]); + + P_SetNewTarget(&((mobj_t *) th)->lastenemy, + mobj_p[P_GetMobj(((mobj_t *)th)->lastenemy,size)]); + } + + { // killough 9/14/98: restore soundtargets + int i; + for (i = 0; i < numsectors; i++) + { + mobj_t *target; + memcpy(&target, save_p, sizeof target); + save_p += sizeof target; + // Must verify soundtarget. See P_ArchiveThinkers. + P_SetNewTarget(§ors[i].soundtarget, mobj_p[P_GetMobj(target,size)]); + } + } + + free(mobj_p); // free translation table + + // killough 3/26/98: Spawn icon landings: + if (gamemode == commercial) + { + // P_SpawnBrainTargets overwrites brain.targeton and brain.easy with zero. + struct brain_s brain_tmp = brain; // saving + + P_SpawnBrainTargets(); + + // old demos with save/load tics should not be affected by this fix + if (!prboom_comp[PC_RESET_MONSTERSPAWNER_PARAMS_AFTER_LOADING].state) + { + brain = brain_tmp; // restoring + } + } +} + +// +// P_ArchiveSpecials +// +enum { + tc_ceiling, + tc_door, + tc_floor, + tc_plat, + tc_flash, + tc_strobe, + tc_glow, + tc_elevator, //jff 2/22/98 new elevator type thinker + tc_scroll, // killough 3/7/98: new scroll effect thinker + tc_pusher, // phares 3/22/98: new push/pull effect thinker + tc_flicker, // killough 10/4/98 + tc_endspecials, + tc_friction // store friction for cl 9 +} specials_e; + +// +// Things to handle: +// +// T_MoveCeiling, (ceiling_t: sector_t * swizzle), - active list +// T_VerticalDoor, (vldoor_t: sector_t * swizzle), +// T_MoveFloor, (floormove_t: sector_t * swizzle), +// T_LightFlash, (lightflash_t: sector_t * swizzle), +// T_StrobeFlash, (strobe_t: sector_t *), +// T_Glow, (glow_t: sector_t *), +// T_PlatRaise, (plat_t: sector_t *), - active list +// T_MoveElevator, (plat_t: sector_t *), - active list // jff 2/22/98 +// T_Scroll // killough 3/7/98 +// T_Pusher // phares 3/22/98 +// T_FireFlicker // killough 10/4/98 +// + +void P_ArchiveSpecials (void) +{ + thinker_t *th; + size_t size = 0; // killough + + // save off the current thinkers (memory size calculation -- killough) + + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + if (!th->function) + { + platlist_t *pl; + ceilinglist_t *cl; //jff 2/22/98 need this for ceilings too now + for (pl=activeplats; pl; pl=pl->next) + if (pl->plat == (plat_t *) th) // killough 2/14/98 + { + size += 4+sizeof(plat_t); + goto end; + } + for (cl=activeceilings; cl; cl=cl->next) // search for activeceiling + if (cl->ceiling == (ceiling_t *) th) //jff 2/22/98 + { + size += 4+sizeof(ceiling_t); + goto end; + } + end:; + } + else + size += + th->function==T_MoveCeiling ? 4+sizeof(ceiling_t) : + th->function==T_VerticalDoor ? 4+sizeof(vldoor_t) : + th->function==T_MoveFloor ? 4+sizeof(floormove_t): + th->function==T_PlatRaise ? 4+sizeof(plat_t) : + th->function==T_LightFlash ? 4+sizeof(lightflash_t): + th->function==T_StrobeFlash ? 4+sizeof(strobe_t) : + th->function==T_Glow ? 4+sizeof(glow_t) : + th->function==T_MoveElevator ? 4+sizeof(elevator_t): + th->function==T_Scroll ? 4+sizeof(scroll_t) : + th->function==T_Pusher ? 4+sizeof(pusher_t) : + th->function==T_FireFlicker? 4+sizeof(fireflicker_t) : + th->function==T_Friction ? 4+sizeof(friction_t) : + 0; + + CheckSaveGame(size + 1); // killough; cph: +1 for the tc_endspecials + + // save off the current thinkers + for (th=thinkercap.next; th!=&thinkercap; th=th->next) + { + if (!th->function) + { + platlist_t *pl; + ceilinglist_t *cl; //jff 2/22/98 add iter variable for ceilings + + // killough 2/8/98: fix plat original height bug. + // Since acv==NULL, this could be a plat in stasis. + // so check the active plats list, and save this + // plat (jff: or ceiling) even if it is in stasis. + + for (pl=activeplats; pl; pl=pl->next) + if (pl->plat == (plat_t *) th) // killough 2/14/98 + goto plat; + + for (cl=activeceilings; cl; cl=cl->next) + if (cl->ceiling == (ceiling_t *) th) //jff 2/22/98 + goto ceiling; + + continue; + } + + if (th->function == T_MoveCeiling) + { + ceiling_t *ceiling; + ceiling: // killough 2/14/98 + *save_p++ = tc_ceiling; + PADSAVEP(); + ceiling = (ceiling_t *)save_p; + memcpy (ceiling, th, sizeof(*ceiling)); + save_p += sizeof(*ceiling); + ceiling->sector = (sector_t *)(intptr_t)(ceiling->sector->iSectorID); + continue; + } + + if (th->function == T_VerticalDoor) + { + vldoor_t *door; + *save_p++ = tc_door; + PADSAVEP(); + door = (vldoor_t *) save_p; + memcpy (door, th, sizeof *door); + save_p += sizeof(*door); + door->sector = (sector_t *)(intptr_t)(door->sector->iSectorID); + //jff 1/31/98 archive line remembered by door as well + door->line = (line_t *) (door->line ? door->line-lines : -1); + continue; + } + + if (th->function == T_MoveFloor) + { + floormove_t *floor; + *save_p++ = tc_floor; + PADSAVEP(); + floor = (floormove_t *)save_p; + memcpy (floor, th, sizeof(*floor)); + save_p += sizeof(*floor); + floor->sector = (sector_t *)(intptr_t)(floor->sector->iSectorID); + continue; + } + + if (th->function == T_PlatRaise) + { + plat_t *plat; + plat: // killough 2/14/98: added fix for original plat height above + *save_p++ = tc_plat; + PADSAVEP(); + plat = (plat_t *)save_p; + memcpy (plat, th, sizeof(*plat)); + save_p += sizeof(*plat); + plat->sector = (sector_t *)(intptr_t)(plat->sector->iSectorID); + continue; + } + + if (th->function == T_LightFlash) + { + lightflash_t *flash; + *save_p++ = tc_flash; + PADSAVEP(); + flash = (lightflash_t *)save_p; + memcpy (flash, th, sizeof(*flash)); + save_p += sizeof(*flash); + flash->sector = (sector_t *)(intptr_t)(flash->sector->iSectorID); + continue; + } + + if (th->function == T_StrobeFlash) + { + strobe_t *strobe; + *save_p++ = tc_strobe; + PADSAVEP(); + strobe = (strobe_t *)save_p; + memcpy (strobe, th, sizeof(*strobe)); + save_p += sizeof(*strobe); + strobe->sector = (sector_t *)(intptr_t)(strobe->sector->iSectorID); + continue; + } + + if (th->function == T_Glow) + { + glow_t *glow; + *save_p++ = tc_glow; + PADSAVEP(); + glow = (glow_t *)save_p; + memcpy (glow, th, sizeof(*glow)); + save_p += sizeof(*glow); + glow->sector = (sector_t *)(intptr_t)(glow->sector->iSectorID); + continue; + } + + // killough 10/4/98: save flickers + if (th->function == T_FireFlicker) + { + fireflicker_t *flicker; + *save_p++ = tc_flicker; + PADSAVEP(); + flicker = (fireflicker_t *)save_p; + memcpy (flicker, th, sizeof(*flicker)); + save_p += sizeof(*flicker); + flicker->sector = (sector_t *)(intptr_t)(flicker->sector->iSectorID); + continue; + } + + //jff 2/22/98 new case for elevators + if (th->function == T_MoveElevator) + { + elevator_t *elevator; //jff 2/22/98 + *save_p++ = tc_elevator; + PADSAVEP(); + elevator = (elevator_t *)save_p; + memcpy (elevator, th, sizeof(*elevator)); + save_p += sizeof(*elevator); + elevator->sector = (sector_t *)(intptr_t)(elevator->sector->iSectorID); + continue; + } + + // killough 3/7/98: Scroll effect thinkers + if (th->function == T_Scroll) + { + *save_p++ = tc_scroll; + memcpy (save_p, th, sizeof(scroll_t)); + save_p += sizeof(scroll_t); + continue; + } + + // phares 3/22/98: Push/Pull effect thinkers + + if (th->function == T_Pusher) + { + *save_p++ = tc_pusher; + memcpy (save_p, th, sizeof(pusher_t)); + save_p += sizeof(pusher_t); + continue; + } + + // store friction for cl 9 + if (th->function == T_Friction) + { + *save_p++ = tc_friction; + PADSAVEP(); + memcpy (save_p, th, sizeof(friction_t)); + save_p += sizeof(friction_t); + continue; + } + } + + // add a terminating marker + *save_p++ = tc_endspecials; +} + + +// +// P_UnArchiveSpecials +// +void P_UnArchiveSpecials (void) +{ + byte tclass; + + // read in saved thinkers + while ((tclass = *save_p++) != tc_endspecials) // killough 2/14/98 + switch (tclass) + { + case tc_ceiling: + PADSAVEP(); + { + ceiling_t *ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVEL, NULL); + memcpy (ceiling, save_p, sizeof(*ceiling)); + save_p += sizeof(*ceiling); + ceiling->sector = §ors[(size_t)ceiling->sector]; + ceiling->sector->ceilingdata = ceiling; //jff 2/22/98 + + if (ceiling->thinker.function) + ceiling->thinker.function = T_MoveCeiling; + + P_AddThinker (&ceiling->thinker); + P_AddActiveCeiling(ceiling); + break; + } + + case tc_door: + PADSAVEP(); + { + vldoor_t *door = Z_Malloc (sizeof(*door), PU_LEVEL, NULL); + memcpy (door, save_p, sizeof(*door)); + save_p += sizeof(*door); + door->sector = §ors[(size_t)door->sector]; + + //jff 1/31/98 unarchive line remembered by door as well + door->line = (intptr_t)door->line!=-1? &lines[(size_t)door->line] : NULL; + + door->sector->ceilingdata = door; //jff 2/22/98 + door->thinker.function = T_VerticalDoor; + P_AddThinker (&door->thinker); + break; + } + + case tc_floor: + PADSAVEP(); + { + floormove_t *floor = Z_Malloc (sizeof(*floor), PU_LEVEL, NULL); + memcpy (floor, save_p, sizeof(*floor)); + save_p += sizeof(*floor); + floor->sector = §ors[(size_t)floor->sector]; + floor->sector->floordata = floor; //jff 2/22/98 + floor->thinker.function = T_MoveFloor; + P_AddThinker (&floor->thinker); + break; + } + + case tc_plat: + PADSAVEP(); + { + plat_t *plat = Z_Malloc (sizeof(*plat), PU_LEVEL, NULL); + memcpy (plat, save_p, sizeof(*plat)); + save_p += sizeof(*plat); + plat->sector = §ors[(size_t)plat->sector]; + plat->sector->floordata = plat; //jff 2/22/98 + + if (plat->thinker.function) + plat->thinker.function = T_PlatRaise; + + P_AddThinker (&plat->thinker); + P_AddActivePlat(plat); + break; + } + + case tc_flash: + PADSAVEP(); + { + lightflash_t *flash = Z_Malloc (sizeof(*flash), PU_LEVEL, NULL); + memcpy (flash, save_p, sizeof(*flash)); + save_p += sizeof(*flash); + flash->sector = §ors[(size_t)flash->sector]; + flash->thinker.function = T_LightFlash; + P_AddThinker (&flash->thinker); + break; + } + + case tc_strobe: + PADSAVEP(); + { + strobe_t *strobe = Z_Malloc (sizeof(*strobe), PU_LEVEL, NULL); + memcpy (strobe, save_p, sizeof(*strobe)); + save_p += sizeof(*strobe); + strobe->sector = §ors[(size_t)strobe->sector]; + strobe->thinker.function = T_StrobeFlash; + P_AddThinker (&strobe->thinker); + break; + } + + case tc_glow: + PADSAVEP(); + { + glow_t *glow = Z_Malloc (sizeof(*glow), PU_LEVEL, NULL); + memcpy (glow, save_p, sizeof(*glow)); + save_p += sizeof(*glow); + glow->sector = §ors[(size_t)glow->sector]; + glow->thinker.function = T_Glow; + P_AddThinker (&glow->thinker); + break; + } + + case tc_flicker: // killough 10/4/98 + PADSAVEP(); + { + fireflicker_t *flicker = Z_Malloc (sizeof(*flicker), PU_LEVEL, NULL); + memcpy (flicker, save_p, sizeof(*flicker)); + save_p += sizeof(*flicker); + flicker->sector = §ors[(size_t)flicker->sector]; + flicker->thinker.function = T_FireFlicker; + P_AddThinker (&flicker->thinker); + break; + } + + //jff 2/22/98 new case for elevators + case tc_elevator: + PADSAVEP(); + { + elevator_t *elevator = Z_Malloc (sizeof(*elevator), PU_LEVEL, NULL); + memcpy (elevator, save_p, sizeof(*elevator)); + save_p += sizeof(*elevator); + elevator->sector = §ors[(size_t)elevator->sector]; + elevator->sector->floordata = elevator; //jff 2/22/98 + elevator->sector->ceilingdata = elevator; //jff 2/22/98 + elevator->thinker.function = T_MoveElevator; + P_AddThinker (&elevator->thinker); + break; + } + + case tc_scroll: // killough 3/7/98: scroll effect thinkers + { + scroll_t *scroll = Z_Malloc (sizeof(scroll_t), PU_LEVEL, NULL); + memcpy (scroll, save_p, sizeof(scroll_t)); + save_p += sizeof(scroll_t); + scroll->thinker.function = T_Scroll; + P_AddThinker(&scroll->thinker); + break; + } + + case tc_pusher: // phares 3/22/98: new Push/Pull effect thinkers + { + pusher_t *pusher = Z_Malloc (sizeof(pusher_t), PU_LEVEL, NULL); + memcpy (pusher, save_p, sizeof(pusher_t)); + save_p += sizeof(pusher_t); + pusher->thinker.function = T_Pusher; + pusher->source = P_GetPushThing(pusher->affectee); + P_AddThinker(&pusher->thinker); + break; + } + + // load friction for cl 9 + case tc_friction: + PADSAVEP(); + { + friction_t *friction = Z_Malloc (sizeof(friction_t), PU_LEVEL, NULL); + memcpy (friction, save_p, sizeof(friction_t)); + save_p += sizeof(friction_t); + friction->thinker.function = T_Friction; + P_AddThinker(&friction->thinker); + break; + } + + default: + I_Error("P_UnarchiveSpecials: Unknown tclass %i in savegame", tclass); + } +} + +// killough 2/16/98: save/restore random number generator state information + +void P_ArchiveRNG(void) +{ + CheckSaveGame(sizeof rng); + memcpy(save_p, &rng, sizeof rng); + save_p += sizeof rng; +} + +void P_UnArchiveRNG(void) +{ + memcpy(&rng, save_p, sizeof rng); + save_p += sizeof rng; +} + +// killough 2/22/98: Save/restore automap state +// killough 2/22/98: Save/restore automap state +void P_ArchiveMap(void) +{ + int i, zero = 0, one = 1; + CheckSaveGame(2 * sizeof zero + sizeof markpointnum + + markpointnum * (sizeof(markpoints[0].x) + sizeof(markpoints[0].y)) + + sizeof automapmode + sizeof one); + + memcpy(save_p, &automapmode, sizeof automapmode); + save_p += sizeof automapmode; + memcpy(save_p, &one, sizeof one); // CPhipps - used to be viewactive, now + save_p += sizeof one; // that's worked out locally by D_Display + memcpy(save_p, &zero, sizeof zero); // CPhipps - used to be followplayer + save_p += sizeof zero; // that is now part of automapmode + memcpy(save_p, &zero, sizeof zero); // CPhipps - used to be automap_grid, ditto + save_p += sizeof zero; + memcpy(save_p, &markpointnum, sizeof markpointnum); + save_p += sizeof markpointnum; + + for (i = 0; i < markpointnum; i++) + { + memcpy(save_p, &markpoints[i].x, sizeof(markpoints[i].x)); + save_p += sizeof(markpoints[i].x); + memcpy(save_p, &markpoints[i].y, sizeof(markpoints[i].y)); + save_p += sizeof(markpoints[i].y); + } +} + +void P_UnArchiveMap(void) +{ + int unused; + memcpy(&automapmode, save_p, sizeof automapmode); + save_p += sizeof automapmode; + memcpy(&unused, save_p, sizeof unused); + save_p += sizeof unused; + memcpy(&unused, save_p, sizeof unused); + save_p += sizeof unused; + memcpy(&unused, save_p, sizeof unused); + save_p += sizeof unused; + + if (automapmode & am_active) + AM_Start(); + + memcpy(&markpointnum, save_p, sizeof markpointnum); + save_p += sizeof markpointnum; + + if (markpointnum) + { + int i; + while (markpointnum >= markpointnum_max) + markpoints = realloc(markpoints, sizeof *markpoints * + (markpointnum_max = markpointnum_max ? markpointnum_max*2 : 16)); + + for (i = 0; i < markpointnum; i++) + { + memcpy(&markpoints[i].x, save_p, sizeof(markpoints[i].x)); + save_p += sizeof(markpoints[i].x); + memcpy(&markpoints[i].y, save_p, sizeof(markpoints[i].y)); + save_p += sizeof(markpoints[i].y); + + AM_setMarkParams(i); + } + } +} + diff --git a/src/p_saveg.h b/src/p_saveg.h new file mode 100644 index 0000000..dd986cf --- /dev/null +++ b/src/p_saveg.h @@ -0,0 +1,66 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Savegame I/O, archiving, persistence. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_SAVEG__ +#define __P_SAVEG__ + +#ifdef __GNUG__ +#pragma interface +#endif + +/* Persistent storage/archiving. + * These are the load / save game routines. */ +void P_ArchivePlayers(void); +void P_UnArchivePlayers(void); +void P_ArchiveWorld(void); +void P_UnArchiveWorld(void); +void P_ArchiveThinkers(void); +void P_UnArchiveThinkers(void); +void P_ArchiveSpecials(void); +void P_UnArchiveSpecials(void); +void P_ThinkerToIndex(void); /* phares 9/13/98: save soundtarget in savegame */ +void P_IndexToThinker(void); /* phares 9/13/98: save soundtarget in savegame */ + +/* 1/18/98 killough: add RNG info to savegame */ +void P_ArchiveRNG(void); +void P_UnArchiveRNG(void); + +/* 2/21/98 killough: add automap info to savegame */ +void P_ArchiveMap(void); +void P_UnArchiveMap(void); + +extern byte *save_p; +void CheckSaveGame(size_t,const char*, int); /* killough */ +#define CheckSaveGame(a) (CheckSaveGame)(a, __FILE__, __LINE__) + +#endif diff --git a/src/p_setup.c b/src/p_setup.c new file mode 100644 index 0000000..da9356c --- /dev/null +++ b/src/p_setup.c @@ -0,0 +1,2867 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Do all the WAD I/O, get map description, + * set up initial state and misc. LUTs. + * + *-----------------------------------------------------------------------------*/ + +#include + +#include "doomstat.h" +#include "m_bbox.h" +#include "m_argv.h" +#include "g_game.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_things.h" +#include "p_maputl.h" +#include "p_map.h" +#include "p_setup.h" +#include "p_spec.h" +#include "p_tick.h" +#include "p_enemy.h" +#include "s_sound.h" +#include "s_advsound.h" +#include "lprintf.h" //jff 10/6/98 for debug outputs +#include "v_video.h" +#include "r_demo.h" +#include "r_fps.h" +#include "hu_tracers.h" +#include "g_overflow.h" +#include "am_map.h" +#include "e6y.h"//e6y + +#include "config.h" +#ifdef HAVE_LIBZ +#include +#endif + +// +// MAP related Lookup tables. +// Store VERTEXES, LINEDEFS, SIDEDEFS, etc. +// + +int numvertexes; +vertex_t *vertexes; + +int numsegs; +seg_t *segs; + +int numsectors; +sector_t *sectors; + +int numsubsectors; +subsector_t *subsectors; + +int numnodes; +node_t *nodes; + +int numlines; +line_t *lines; + +int numsides; +side_t *sides; + +int *sslines_indexes; +ssline_t *sslines; + +byte *map_subsectors; + +//////////////////////////////////////////////////////////////////////////////////////////// +// figgi 08/21/00 -- constants and globals for glBsp support +#define GL_VERT_OFFSET 4 + +int firstglvertex = 0; +int nodesVersion = 0; +dboolean forceOldBsp = false; + +// figgi 08/21/00 -- glSegs +typedef struct +{ + unsigned short v1; // start vertex (16 bit) + unsigned short v2; // end vertex (16 bit) + unsigned short linedef; // linedef, or -1 for minisegs + short side; // side on linedef: 0 for right, 1 for left + unsigned short partner; // corresponding partner seg, or -1 on one-sided walls +} glseg_t; + +// fixed 32 bit gl_vert format v2.0+ (glBsp 1.91) +typedef struct +{ + fixed_t x,y; +} mapglvertex_t; + +enum +{ + ML_GL_LABEL=0, // A separator name, GL_ExMx or GL_MAPxx + ML_GL_VERTS, // Extra Vertices + ML_GL_SEGS, // Segs, from linedefs & minisegs + ML_GL_SSECT, // SubSectors, list of segs + ML_GL_NODES // GL BSP nodes +}; +//////////////////////////////////////////////////////////////////////////////////////////// + + +// 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, bmapheight; // size in mapblocks + +// killough 3/1/98: remove blockmap limit internally: +int *blockmap; // was short -- killough + +// offsets in blockmap are from here +int *blockmaplump; // was short -- killough + +fixed_t bmaporgx, bmaporgy; // origin of block map + +mobj_t **blocklinks; // for thing chains + +// MAES: extensions to support 512x512 blockmaps. +// They represent the maximum negative number which represents +// a positive offset, otherwise they are left at -257, which +// never triggers a check. +// If a blockmap index is ever LE than either, then +// its actual value is to be interpreted as 0x01FF&x. +// Full 512x512 blockmaps get this value set to -1. +// A 511x511 blockmap would still have a valid negative number +// e.g. -1..510, so they would be set to -2 +// Non-extreme maps remain unaffected. +int blockmapxneg = -257; +int blockmapyneg = -257; + +// +// REJECT +// For fast sight rejection. +// Speeds up enemy AI by skipping detailed +// LineOf Sight calculation. +// Without the special effect, this could +// be used as a PVS lookup as well. +// + +static int rejectlump = -1;// cph - store reject lump num if cached +const byte *rejectmatrix; // cph - const* + +// Maintain single and multi player starting spots. + +// 1/11/98 killough: Remove limit on deathmatch starts +mapthing_t *deathmatchstarts; // killough +size_t num_deathmatchstarts; // killough + +mapthing_t *deathmatch_p; +mapthing_t playerstarts[MAXPLAYERS]; + +static int current_episode = -1; +static int current_map = -1; +static int current_nodesVersion = -1; +static int samelevel = false; + +// e6y: Smart malloc +// Used by P_SetupLevel() for smart data loading +// Do nothing if level is the same +static void *malloc_IfSameLevel(void* p, size_t size) +{ + if (!samelevel || !p) + { + return malloc(size); + } + return p; +} + +// e6y: Smart calloc +// Used by P_SetupLevel() for smart data loading +// Clear the memory without allocation if level is the same +static void *calloc_IfSameLevel(void* p, size_t n1, size_t n2) +{ + if (!samelevel) + { + return calloc(n1, n2); + } + else + { + memset(p, 0, n1 * n2); + return p; + } +} + +// +// CheckForIdentifier +// Checks a lump for a magic string to identify its type (e.g. extended nodes) +// + +static dboolean CheckForIdentifier(int lumpnum, const byte *id, size_t length) +{ + dboolean result = false; + + if (W_LumpLength(lumpnum) >= length) + { + const char *data = W_CacheLumpNum(lumpnum); + + if (!memcmp(data, id, length)) + result = true; + + W_UnlockLumpNum(lumpnum); + } + + return result; +} + +// +// P_CheckForZDoomNodes +// + +static dboolean P_CheckForZDoomNodes(int lumpnum, int gl_lumpnum) +{ +#ifndef HAVE_LIBZ + if (CheckForIdentifier(lumpnum + ML_NODES, "ZNOD", 4)) + I_Error("P_CheckForZDoomNodes: compressed ZDoom nodes not supported yet"); +#endif + + if (CheckForIdentifier(lumpnum + ML_SSECTORS, "ZGLN", 4)) + I_Error("P_CheckForZDoomNodes: ZDoom GL nodes not supported yet"); + + return false; +} + +// +// P_CheckForDeePBSPv4Nodes +// http://www.sbsoftware.com/files/DeePBSPV4specs.txt +// + +static dboolean P_CheckForDeePBSPv4Nodes(int lumpnum, int gl_lumpnum) +{ + int result = CheckForIdentifier(lumpnum + ML_NODES, "xNd4\0\0\0\0", 8); + + if (result) + lprintf(LO_INFO, "P_CheckForDeePBSPv4Nodes: DeePBSP v4 Extended nodes are detected\n"); + + return result; +} + +// +// P_CheckForZDoomUncompressedNodes +// http://zdoom.org/wiki/ZDBSP#Compressed_Nodes +// + +enum { + NO_ZDOOM_NODES, + ZDOOM_XNOD_NODES, + ZDOOM_ZNOD_NODES +}; + +static int P_CheckForZDoomUncompressedNodes(int lumpnum, int gl_lumpnum) +{ + int ret = NO_ZDOOM_NODES; + int result = CheckForIdentifier(lumpnum + ML_NODES, "XNOD", 4); + + if (result) + { + lprintf(LO_INFO, "P_CheckForZDoomUncompressedNodes: ZDoom uncompressed normal nodes are detected\n"); + ret = ZDOOM_XNOD_NODES; + } +#ifdef HAVE_LIBZ + else + { + result = CheckForIdentifier(lumpnum + ML_NODES, "ZNOD", 4); + + if (result) + { + lprintf(LO_INFO, "P_CheckForZDoomUncompressedNodes: compressed ZDoom nodes are detected\n"); + ret = ZDOOM_ZNOD_NODES; + } + } +#endif + + return ret; +} + +// +// P_GetNodesVersion +// + +static void P_GetNodesVersion(int lumpnum, int gl_lumpnum) +{ + int ver = -1; + nodesVersion = 0; + + if ( (gl_lumpnum > lumpnum) && (forceOldBsp == false) && (compatibility_level >= prboom_2_compatibility) ) + { + if (CheckForIdentifier(gl_lumpnum+ML_GL_VERTS, "gNd2", 4)) { + if (CheckForIdentifier(gl_lumpnum+ML_GL_SEGS, "gNd3", 4)) { + ver = 3; + } else { + nodesVersion = 2; + lprintf(LO_DEBUG, "P_GetNodesVersion: found version 2 nodes\n"); + } + } + else if (CheckForIdentifier(gl_lumpnum+ML_GL_VERTS, "gNd4", 4)) { + ver = 4; + } + else if (CheckForIdentifier(gl_lumpnum+ML_GL_VERTS, "gNd5", 4)) { + ver = 5; + } + //e6y: unknown gl nodes will be ignored + if (nodesVersion == 0 && ver != -1) + { + lprintf(LO_DEBUG,"P_GetNodesVersion: found version %d nodes\n", ver); + lprintf(LO_DEBUG,"P_GetNodesVersion: version %d nodes not supported\n", ver); + } + } else { + nodesVersion = 0; + lprintf(LO_DEBUG,"P_GetNodesVersion: using normal BSP nodes\n"); + if (P_CheckForZDoomNodes(lumpnum, gl_lumpnum)) + I_Error("P_GetNodesVersion: ZDoom nodes not supported yet"); + } +} + +// +// P_LoadVertexes +// +// killough 5/3/98: reformatted, cleaned up +// +static void P_LoadVertexes (int lump) +{ + const mapvertex_t *data; // cph - const + int i; + + // Determine number of lumps: + // total lump length / vertex record length. + numvertexes = W_LumpLength(lump) / sizeof(mapvertex_t); + + // Allocate zone memory for buffer. + vertexes = calloc_IfSameLevel(vertexes, numvertexes, sizeof(vertex_t)); + + // Load data into cache. + // cph 2006/07/29 - cast to mapvertex_t here, making the loop below much neater + data = (const mapvertex_t *)W_CacheLumpNum(lump); + + // Copy and convert vertex coordinates, + // internal representation as fixed. + for (i=0; i= 0) // check for glVertices + { + gldata = W_CacheLumpNum(gllump); + + if (nodesVersion == 2) // 32 bit GL_VERT format (16.16 fixed) + { + const mapglvertex_t* mgl; + + numvertexes += (W_LumpLength(gllump) - GL_VERT_OFFSET)/sizeof(mapglvertex_t); + vertexes = malloc_IfSameLevel(vertexes, numvertexes * sizeof(vertex_t)); + mgl = (const mapglvertex_t *) (gldata + GL_VERT_OFFSET); + + for (i = firstglvertex; i < numvertexes; i++) + { + vertexes[i].x = mgl->x; + vertexes[i].y = mgl->y; + mgl++; + } + } + else + { + numvertexes += W_LumpLength(gllump)/sizeof(mapvertex_t); + vertexes = malloc_IfSameLevel(vertexes, numvertexes * sizeof(vertex_t)); + ml = (const mapvertex_t *)gldata; + + for (i = firstglvertex; i < numvertexes; i++) + { + vertexes[i].x = LittleShort(ml->x)<y)<x)<y)<x - v2->x) / (float)FRACUNIT; + b = (float)(v1->y - v2->y) / (float)FRACUNIT; + r = (int)(sqrt(a*a+b*b) * (float)FRACUNIT); + return r; +} + + + +// +// P_LoadSegs +// +// killough 5/3/98: reformatted, cleaned up + +static void P_LoadSegs (int lump) +{ + int i; + const mapseg_t *data; // cph - const + + numsegs = W_LumpLength(lump) / sizeof(mapseg_t); + segs = calloc_IfSameLevel(segs, numsegs, sizeof(seg_t)); + data = (const mapseg_t *)W_CacheLumpNum(lump); // cph - wad lump handling updated + + if ((!data) || (!numsegs)) + I_Error("P_LoadSegs: no segs in level"); + + for (i=0; iv1); + v2 = (unsigned short)LittleShort(ml->v2); + + // e6y + // moved down for additional checks to avoid overflow + // if wrong vertexe's indexes are in SEGS lump + // see below for more detailed information + //li->v1 = &vertexes[v1]; + //li->v2 = &vertexes[v2]; + + li->miniseg = false; // figgi -- there are no minisegs in classic BSP nodes + + // e6y: moved down, see below + //li->length = GetDistance(li->v2->x - li->v1->x, li->v2->y - li->v1->y); + + li->angle = (LittleShort(ml->angle))<<16; + li->offset =(LittleShort(ml->offset))<<16; + linedef = (unsigned short)LittleShort(ml->linedef); + + //e6y: check for wrong indexes + if ((unsigned)linedef >= (unsigned)numlines) + { + I_Error("P_LoadSegs: seg %d references a non-existent linedef %d", + i, (unsigned)linedef); + } + + ldef = &lines[linedef]; + li->linedef = ldef; + side = LittleShort(ml->side); + + //e6y: fix wrong side index + if (side != 0 && side != 1) + { + lprintf(LO_WARN, "P_LoadSegs: seg %d contains wrong side index %d. Replaced with 1.\n", i, side); + side = 1; + } + + //e6y: check for wrong indexes + if ((unsigned)ldef->sidenum[side] >= (unsigned)numsides) + { + I_Error("P_LoadSegs: linedef %d for seg %d references a non-existent sidedef %d", + linedef, i, (unsigned)ldef->sidenum[side]); + } + + li->sidedef = &sides[ldef->sidenum[side]]; + + /* cph 2006/09/30 - our frontsector can be the second side of the + * linedef, so must check for NO_INDEX in case we are incorrectly + * referencing the back of a 1S line */ + if (ldef->sidenum[side] != NO_INDEX) + li->frontsector = sides[ldef->sidenum[side]].sector; + else { + li->frontsector = 0; + lprintf(LO_WARN, "P_LoadSegs: front of seg %i has no sidedef\n", i); + } + + if (ldef->flags & ML_TWOSIDED) + { + int sidenum = ldef->sidenum[side ^ 1]; + + if (sidenum == NO_INDEX) + { + // this is wrong + li->backsector = GetSectorAtNullAddress(); + } + else + { + li->backsector = sides[sidenum].sector; + } + } + else + { + li->backsector = 0; + } + + // e6y + // check and fix wrong references to non-existent vertexes + // see e1m9 @ NIVELES.WAD + // http://www.doomworld.com/idgames/index.php?id=12647 + if (v1 >= numvertexes || v2 >= numvertexes) + { + char str[200] = + "P_LoadSegs: compatibility loss - seg %d references a non-existent vertex %d\n"; + + if (demorecording) + { + I_Error(strcat(str, "Demo recording on levels with invalid nodes is not allowed"), + i, (v1 >= numvertexes ? v1 : v2)); + } + + if (v1 >= numvertexes) + lprintf(LO_WARN, str, i, v1); + if (v2 >= numvertexes) + lprintf(LO_WARN, str, i, v2); + + if (li->sidedef == &sides[li->linedef->sidenum[0]]) + { + li->v1 = lines[ml->linedef].v1; + li->v2 = lines[ml->linedef].v2; + } + else + { + li->v1 = lines[ml->linedef].v2; + li->v2 = lines[ml->linedef].v1; + } + } + else + { + li->v1 = &vertexes[v1]; + li->v2 = &vertexes[v2]; + } + + // Recalculate seg offsets that are sometimes incorrect + // with certain nodebuilders. Fixes among others, line 20365 + // of DV.wad, map 5 + li->offset = GetOffset(li->v1, (ml->side ? ldef->v2 : ldef->v1)); + } + + W_UnlockLumpNum(lump); // cph - release the data +} + +static void P_LoadSegs_V4(int lump) +{ + int i; + const mapseg_v4_t *data; + + numsegs = W_LumpLength(lump) / sizeof(mapseg_v4_t); + segs = calloc_IfSameLevel(segs, numsegs, sizeof(seg_t)); + data = (const mapseg_v4_t *)W_CacheLumpNum(lump); + + if ((!data) || (!numsegs)) + I_Error("P_LoadSegs_V4: no segs in level"); + + for (i = 0; i < numsegs; i++) + { + seg_t *li = segs+i; + const mapseg_v4_t *ml = data + i; + int v1, v2; + + int side, linedef; + line_t *ldef; + + // MB 2020-04-22: Fix endianess for DeePBSP V4 extended nodes + v1 = LittleLong(ml->v1); + v2 = LittleLong(ml->v2); + + li->miniseg = false; // figgi -- there are no minisegs in classic BSP nodes + + li->angle = (LittleShort(ml->angle))<<16; + li->offset =(LittleShort(ml->offset))<<16; + linedef = (unsigned short)LittleShort(ml->linedef); + + //e6y: check for wrong indexes + if ((unsigned)linedef >= (unsigned)numlines) + { + I_Error("P_LoadSegs_V4: seg %d references a non-existent linedef %d", + i, (unsigned)linedef); + } + + ldef = &lines[linedef]; + li->linedef = ldef; + side = LittleShort(ml->side); + + //e6y: fix wrong side index + if (side != 0 && side != 1) + { + lprintf(LO_WARN, "P_LoadSegs_V4: seg %d contains wrong side index %d. Replaced with 1.\n", i, side); + side = 1; + } + + //e6y: check for wrong indexes + if ((unsigned)ldef->sidenum[side] >= (unsigned)numsides) + { + I_Error("P_LoadSegs_V4: linedef %d for seg %d references a non-existent sidedef %d", + linedef, i, (unsigned)ldef->sidenum[side]); + } + + li->sidedef = &sides[ldef->sidenum[side]]; + + /* cph 2006/09/30 - our frontsector can be the second side of the + * linedef, so must check for NO_INDEX in case we are incorrectly + * referencing the back of a 1S line */ + if (ldef->sidenum[side] != NO_INDEX) + { + li->frontsector = sides[ldef->sidenum[side]].sector; + } + else + { + li->frontsector = 0; + lprintf(LO_WARN, "P_LoadSegs_V4: front of seg %i has no sidedef\n", i); + } + + if (ldef->flags & ML_TWOSIDED && ldef->sidenum[side^1]!=NO_INDEX) + li->backsector = sides[ldef->sidenum[side^1]].sector; + else + li->backsector = 0; + + // e6y + // check and fix wrong references to non-existent vertexes + // see e1m9 @ NIVELES.WAD + // http://www.doomworld.com/idgames/index.php?id=12647 + if (v1 >= numvertexes || v2 >= numvertexes) + { + char str[200] = + "P_LoadSegs_V4: compatibility loss - seg %d references a non-existent vertex %d\n"; + + if (demorecording) + { + I_Error(strcat(str, "Demo recording on levels with invalid nodes is not allowed"), + i, (v1 >= numvertexes ? v1 : v2)); + } + + if (v1 >= numvertexes) + lprintf(LO_WARN, str, i, v1); + if (v2 >= numvertexes) + lprintf(LO_WARN, str, i, v2); + + if (li->sidedef == &sides[li->linedef->sidenum[0]]) + { + li->v1 = lines[ml->linedef].v1; + li->v2 = lines[ml->linedef].v2; + } + else + { + li->v1 = lines[ml->linedef].v2; + li->v2 = lines[ml->linedef].v1; + } + } + else + { + li->v1 = &vertexes[v1]; + li->v2 = &vertexes[v2]; + } + + // Recalculate seg offsets that are sometimes incorrect + // with certain nodebuilders. Fixes among others, line 20365 + // of DV.wad, map 5 + li->offset = GetOffset(li->v1, (ml->side ? ldef->v2 : ldef->v1)); + } + + W_UnlockLumpNum(lump); // cph - release the data +} + + +/******************************************* + * Name : P_LoadGLSegs * + * created : 08/13/00 * + * modified : 09/18/00, adapted for PrBoom * + * author : figgi * + * what : support for gl nodes * + *******************************************/ +static void P_LoadGLSegs(int lump) +{ + int i; + const glseg_t *ml; + line_t *ldef; + + numsegs = W_LumpLength(lump) / sizeof(glseg_t); + segs = malloc_IfSameLevel(segs, numsegs * sizeof(seg_t)); + memset(segs, 0, numsegs * sizeof(seg_t)); + ml = (const glseg_t*)W_CacheLumpNum(lump); + + if ((!ml) || (!numsegs)) + I_Error("P_LoadGLSegs: no glsegs in level"); + + for(i = 0; i < numsegs; i++) + { // check for gl-vertices + segs[i].v1 = &vertexes[checkGLVertex(LittleShort(ml->v1))]; + segs[i].v2 = &vertexes[checkGLVertex(LittleShort(ml->v2))]; + + if(ml->linedef != (unsigned short)-1) // skip minisegs + { + ldef = &lines[ml->linedef]; + segs[i].linedef = ldef; + segs[i].miniseg = false; + segs[i].angle = R_PointToAngle2(segs[i].v1->x,segs[i].v1->y,segs[i].v2->x,segs[i].v2->y); + + segs[i].sidedef = &sides[ldef->sidenum[ml->side]]; + segs[i].frontsector = sides[ldef->sidenum[ml->side]].sector; + if (ldef->flags & ML_TWOSIDED) + segs[i].backsector = sides[ldef->sidenum[ml->side^1]].sector; + else + segs[i].backsector = 0; + + if (ml->side) + segs[i].offset = GetOffset(segs[i].v1, ldef->v2); + else + segs[i].offset = GetOffset(segs[i].v1, ldef->v1); + } + else + { + segs[i].miniseg = true; + segs[i].angle = 0; + segs[i].offset = 0; + segs[i].linedef = NULL; + segs[i].sidedef = NULL; + segs[i].frontsector = NULL; + segs[i].backsector = NULL; + } + ml++; + } + W_UnlockLumpNum(lump); +} + +// +// P_LoadSubsectors +// +// killough 5/3/98: reformatted, cleaned up + +static void P_LoadSubsectors (int lump) +{ + /* cph 2006/07/29 - make data a const mapsubsector_t *, so the loop below is simpler & gives no constness warnings */ + const mapsubsector_t *data; + int i; + + numsubsectors = W_LumpLength (lump) / sizeof(mapsubsector_t); + subsectors = calloc_IfSameLevel(subsectors, numsubsectors, sizeof(subsector_t)); + data = (const mapsubsector_t *)W_CacheLumpNum(lump); + + if ((!data) || (!numsubsectors)) + I_Error("P_LoadSubsectors: no subsectors in level"); + + for (i=0; iiSectorID=i; // proff 04/05/2000: needed for OpenGL + ss->floorheight = LittleShort(ms->floorheight)<ceilingheight = LittleShort(ms->ceilingheight)<floorpic = R_FlatNumForName(ms->floorpic); + ss->ceilingpic = R_FlatNumForName(ms->ceilingpic); + ss->lightlevel = LittleShort(ms->lightlevel); + ss->special = LittleShort(ms->special); + ss->oldspecial = LittleShort(ms->special); + ss->tag = LittleShort(ms->tag); + ss->thinglist = NULL; + ss->touching_thinglist = NULL; // phares 3/14/98 + + ss->nextsec = -1; //jff 2/26/98 add fields to support locking out + ss->prevsec = -1; // stair retriggering until build completes + + // killough 3/7/98: + ss->floor_xoffs = 0; + ss->floor_yoffs = 0; // floor and ceiling flats offsets + ss->ceiling_xoffs = 0; + ss->ceiling_yoffs = 0; + ss->heightsec = -1; // sector used to get floor and ceiling height + ss->floorlightsec = -1; // sector used to get floor lighting + // killough 3/7/98: end changes + + // killough 4/11/98 sector used to get ceiling lighting: + ss->ceilinglightsec = -1; + + // killough 4/4/98: colormaps: + ss->bottommap = ss->midmap = ss->topmap = 0; + + // killough 10/98: sky textures coming from sidedefs: + ss->sky = 0; + + // [kb] For R_WiggleFix + ss->cachedheight = 0; + ss->scaleindex = 0; + } + + W_UnlockLumpNum(lump); // cph - release the data +} + + +// +// P_LoadNodes +// +// killough 5/3/98: reformatted, cleaned up + +static void P_LoadNodes (int lump) +{ + const byte *data; // cph - const* + int i; + + numnodes = W_LumpLength (lump) / sizeof(mapnode_t); + nodes = malloc_IfSameLevel(nodes, numnodes * sizeof(node_t)); + data = W_CacheLumpNum (lump); // cph - wad lump handling updated + + if ((!data) || (!numnodes)) + { + // allow trivial maps + if (numsubsectors == 1) + lprintf(LO_INFO, + "P_LoadNodes: trivial map (no nodes, one subsector)\n"); + else + I_Error("P_LoadNodes: no nodes in level"); + } + + for (i=0; ix = LittleShort(mn->x)<y = LittleShort(mn->y)<dx = LittleShort(mn->dx)<dy = LittleShort(mn->dy)<children[j] = (unsigned short)LittleShort(mn->children[j]); + + // e6y: support for extended nodes + if (no->children[j] == 0xFFFF) + { + no->children[j] = -1; + } + else if (no->children[j] & 0x8000) + { + // Convert to extended type + no->children[j] &= ~0x8000; + + // haleyjd 11/06/10: check for invalid subsector reference + if(no->children[j] >= numsubsectors) + { + lprintf(LO_ERROR, "P_LoadNodes: BSP tree references invalid subsector %d.\n", no->children[j]); + no->children[j] = 0; + } + + no->children[j] |= NF_SUBSECTOR; + } + + for (k=0 ; k<4 ; k++) + no->bbox[j][k] = LittleShort(mn->bbox[j][k])<x = LittleShort(mn->x)<y = LittleShort(mn->y)<dx = LittleShort(mn->dx)<dy = LittleShort(mn->dy)<children[j] = LittleLong(mn->children[j]); + + for (k=0 ; k<4 ; k++) + no->bbox[j][k] = LittleShort(mn->bbox[j][k])<v1); + v2 = LittleLong(ml->v2); + + li->miniseg = false; + + linedef = (unsigned short)LittleShort(ml->linedef); + + //e6y: check for wrong indexes + if ((unsigned int)linedef >= (unsigned int)numlines) + { + I_Error("P_LoadZSegs: seg %d references a non-existent linedef %d", + i, (unsigned)linedef); + } + + ldef = &lines[linedef]; + li->linedef = ldef; + side = ml->side; + + //e6y: fix wrong side index + if (side != 0 && side != 1) + { + lprintf(LO_WARN, "P_LoadZSegs: seg %d contains wrong side index %d. Replaced with 1.\n", i, side); + side = 1; + } + + //e6y: check for wrong indexes + if ((unsigned)ldef->sidenum[side] >= (unsigned)numsides) + { + I_Error("P_LoadZSegs: linedef %d for seg %d references a non-existent sidedef %d", + linedef, i, (unsigned)ldef->sidenum[side]); + } + + li->sidedef = &sides[ldef->sidenum[side]]; + + /* cph 2006/09/30 - our frontsector can be the second side of the + * linedef, so must check for NO_INDEX in case we are incorrectly + * referencing the back of a 1S line */ + if (ldef->sidenum[side] != NO_INDEX) + { + li->frontsector = sides[ldef->sidenum[side]].sector; + } + else + { + li->frontsector = 0; + lprintf(LO_WARN, "P_LoadZSegs: front of seg %i has no sidedef\n", i); + } + + if ((ldef->flags & ML_TWOSIDED) && (ldef->sidenum[side^1] != NO_INDEX)) + li->backsector = sides[ldef->sidenum[side^1]].sector; + else + li->backsector = 0; + + li->v1 = &vertexes[v1]; + li->v2 = &vertexes[v2]; + + li->offset = GetOffset(li->v1, (side ? ldef->v2 : ldef->v1)); + li->angle = R_PointToAngle2(segs[i].v1->x, segs[i].v1->y, segs[i].v2->x, segs[i].v2->y); + //li->angle = (int)((float)atan2(li->v2->y - li->v1->y,li->v2->x - li->v1->x) * (ANG180 / M_PI)); + } +} + +// MB 2020-03-01: Fix endianess for 32-bit ZDoom nodes +// https://zdoom.org/wiki/Node#ZDoom_extended_nodes +static void P_LoadZNodes(int lump, int glnodes, int compressed) +{ + byte *data; + unsigned int i; + int len; + + unsigned int orgVerts, newVerts; + unsigned int numSubs, currSeg; + unsigned int numSegs; + unsigned int numNodes; + vertex_t *newvertarray = NULL; +#ifdef HAVE_LIBZ + byte *output; +#endif + + data = W_CacheLumpNum(lump); + len = W_LumpLength(lump); + + if (compressed == ZDOOM_ZNOD_NODES) + { +#ifdef HAVE_LIBZ + int outlen, err; + z_stream *zstream; + + // first estimate for compression rate: + // output buffer size == 2.5 * input size + outlen = 2.5 * len; + output = Z_Malloc(outlen, PU_STATIC, 0); + + // initialize stream state for decompression + zstream = malloc(sizeof(*zstream)); + memset(zstream, 0, sizeof(*zstream)); + zstream->next_in = data + 4; + zstream->avail_in = len - 4; + zstream->next_out = output; + zstream->avail_out = outlen; + + if (inflateInit(zstream) != Z_OK) + I_Error("P_LoadZNodes: Error during ZDoom nodes decompression initialization!"); + + // resize if output buffer runs full + while ((err = inflate(zstream, Z_SYNC_FLUSH)) == Z_OK) + { + int outlen_old = outlen; + outlen = 2 * outlen_old; + output = realloc(output, outlen); + zstream->next_out = output + outlen_old; + zstream->avail_out = outlen - outlen_old; + } + + if (err != Z_STREAM_END) + I_Error("P_LoadZNodes: Error during ZDoom nodes decompression!"); + + lprintf(LO_INFO, "P_LoadZNodes: ZDoom nodes compression ratio %.3f\n", + (float)zstream->total_out/zstream->total_in); + + data = output; + len = zstream->total_out; + + if (inflateEnd(zstream) != Z_OK) + I_Error("P_LoadZNodes: Error during ZDoom nodes decompression shut-down!"); + + // release the original data lump + W_UnlockLumpNum(lump); + free(zstream); +#else + I_Error("P_LoadZNodes: Compressed ZDoom nodes are not supported!"); +#endif + } + else + { + // skip header + CheckZNodesOverflow(&len, 4); + data += 4; + } + + // Read extra vertices added during node building + CheckZNodesOverflow(&len, sizeof(orgVerts)); + orgVerts = LittleLong(*((const unsigned int*)data)); + data += sizeof(orgVerts); + + CheckZNodesOverflow(&len, sizeof(newVerts)); + newVerts = LittleLong(*((const unsigned int*)data)); + data += sizeof(newVerts); + + if (!samelevel) + { + if (orgVerts + newVerts == (unsigned int)numvertexes) + { + newvertarray = vertexes; + } + else + { + newvertarray = calloc(orgVerts + newVerts, sizeof(vertex_t)); + memcpy (newvertarray, vertexes, orgVerts * sizeof(vertex_t)); + } + + CheckZNodesOverflow(&len, newVerts * (sizeof(newvertarray[0].x) + sizeof(newvertarray[0].y))); + for (i = 0; i < newVerts; i++) + { + newvertarray[i + orgVerts].x = LittleLong(*((const unsigned int*)data)); + data += sizeof(newvertarray[0].x); + + newvertarray[i + orgVerts].y = LittleLong(*((const unsigned int*)data)); + data += sizeof(newvertarray[0].y); + } + + if (vertexes != newvertarray) + { + for (i = 0; i < (unsigned int)numlines; i++) + { + lines[i].v1 = lines[i].v1 - vertexes + newvertarray; + lines[i].v2 = lines[i].v2 - vertexes + newvertarray; + } + free(vertexes); + vertexes = newvertarray; + numvertexes = orgVerts + newVerts; + } + } + else + { + int size = newVerts * (sizeof(newvertarray[0].x) + sizeof(newvertarray[0].y)); + CheckZNodesOverflow(&len, size); + data += size; + + // P_LoadVertexes reset numvertexes, need to increase it again + numvertexes = orgVerts + newVerts; + } + + // Read the subsectors + CheckZNodesOverflow(&len, sizeof(numSubs)); + numSubs = LittleLong(*((const unsigned int*)data)); + data += sizeof(numSubs); + + numsubsectors = numSubs; + if (numsubsectors <= 0) + I_Error("P_LoadZNodes: no subsectors in level"); + subsectors = calloc_IfSameLevel(subsectors, numsubsectors, sizeof(subsector_t)); + + CheckZNodesOverflow(&len, numSubs * sizeof(mapsubsector_znod_t)); + // MB 2020-03-01 + // First segment number of each subsector is not stored + // First subsector starts at segment 0 + // Subsequent subsectors starts with the next unused segment number (currSeg) + for (i = currSeg = 0; i < numSubs; i++) + { + const mapsubsector_znod_t *mseg = (const mapsubsector_znod_t *) data + i; + + subsectors[i].firstline = currSeg; + subsectors[i].numlines = LittleLong(mseg->numsegs); + currSeg += LittleLong(mseg->numsegs); + } + data += numSubs * sizeof(mapsubsector_znod_t); + + // Read the segs + CheckZNodesOverflow(&len, sizeof(numSegs)); + numSegs = LittleLong(*((const unsigned int*)data)); + data += sizeof(numSegs); + + // The number of segs stored should match the number of + // segs used by subsectors. + if (numSegs != currSeg) + { + I_Error("P_LoadZNodes: Incorrect number of segs in nodes."); + } + + numsegs = numSegs; + segs = calloc_IfSameLevel(segs, numsegs, sizeof(seg_t)); + + if (glnodes == 0) + { + CheckZNodesOverflow(&len, numsegs * sizeof(mapseg_znod_t)); + P_LoadZSegs(data); + data += numsegs * sizeof(mapseg_znod_t); + } + else + { + //P_LoadGLZSegs (data, glnodes); + I_Error("P_LoadZNodes: GL segs are not supported."); + } + + // Read nodes + CheckZNodesOverflow(&len, sizeof(numNodes)); + numNodes = LittleLong(*((const unsigned int*)data)); + data += sizeof(numNodes); + + numnodes = numNodes; + nodes = calloc_IfSameLevel(nodes, numNodes, sizeof(node_t)); + + CheckZNodesOverflow(&len, numNodes * sizeof(mapnode_znod_t)); + for (i = 0; i < numNodes; i++) + { + int j, k; + node_t *no = nodes + i; + const mapnode_znod_t *mn = (const mapnode_znod_t *) data + i; + + no->x = LittleShort(mn->x)<y = LittleShort(mn->y)<dx = LittleShort(mn->dx)<dy = LittleShort(mn->dy)<children[j] = LittleLong(mn->children[j]); + + for (k = 0; k < 4; k++) + no->bbox[j][k] = LittleShort(mn->bbox[j][k])<x + (mobj->y >> 16)) +static int C_DECL dicmp_sprite_by_pos(const void *a, const void *b) +{ + const mobj_t *m1 = (*((const mobj_t * const *)a)); + const mobj_t *m2 = (*((const mobj_t * const *)b)); + + int res = GETXY(m2) - GETXY(m1); + no_overlapped_sprites = no_overlapped_sprites && res; + return res; +} +#endif + +/* + * P_LoadThings + * + * killough 5/3/98: reformatted, cleaned up + * cph 2001/07/07 - don't write into the lump cache, especially non-idepotent + * changes like byte order reversals. Take a copy to edit. + */ + +static void P_LoadThings (int lump) +{ + int i, numthings = W_LumpLength (lump) / sizeof(mapthing_t); + const mapthing_t *data = W_CacheLumpNum (lump); + + mobj_t *mobj; + int mobjcount = 0; + mobj_t **mobjlist = malloc(numthings * sizeof(mobjlist[0])); + + if ((!data) || (!numthings)) + I_Error("P_LoadThings: no things in level"); + + for (i=0; iinfo->speed == 0) + mobjlist[mobjcount++] = mobj; + } + + W_UnlockLumpNum(lump); // cph - release the data + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + no_overlapped_sprites = true; + qsort(mobjlist, mobjcount, sizeof(mobjlist[0]), dicmp_sprite_by_pos); + if (!no_overlapped_sprites) + { + i = 1; + while (i < mobjcount) + { + mobj_t *m1 = mobjlist[i - 1]; + mobj_t *m2 = mobjlist[i - 0]; + + if (GETXY(m1) == GETXY(m2)) + { + mobj_t *mo = (m1->index < m2->index ? m1 : m2); + i++; + while (i < mobjcount && GETXY(mobjlist[i]) == GETXY(m1)) + { + if (mobjlist[i]->index < mo->index) + { + mo = mobjlist[i]; + } + i++; + } + + // 'nearest' + mo->flags |= MF_FOREGROUND; + } + i++; + } + } + } +#endif + + free(mobjlist); +} + +// +// P_LoadLineDefs +// Also counts secret lines for intermissions. +// ^^^ +// ??? killough ??? +// Does this mean secrets used to be linedef-based, rather than sector-based? +// +// killough 4/4/98: split into two functions, to allow sidedef overloading +// +// killough 5/3/98: reformatted, cleaned up + +static void P_LoadLineDefs (int lump) +{ + const byte *data; // cph - const* + int i; + + numlines = W_LumpLength (lump) / sizeof(maplinedef_t); + lines = calloc_IfSameLevel(lines, numlines, sizeof(line_t)); + data = W_CacheLumpNum (lump); // cph - wad lump handling updated + + for (i=0; iflags = (unsigned short)LittleShort(mld->flags); + ld->special = LittleShort(mld->special); + ld->tag = LittleShort(mld->tag); + v1 = ld->v1 = &vertexes[(unsigned short)LittleShort(mld->v1)]; + v2 = ld->v2 = &vertexes[(unsigned short)LittleShort(mld->v2)]; + ld->dx = v2->x - v1->x; + ld->dy = v2->y - v1->y; +#ifdef GL_DOOM + // e6y + // Rounding the wall length to the nearest integer + // when determining length instead of always rounding down + // There is no more glitches on seams between identical textures. + ld->texel_length = GetTexelDistance(ld->dx, ld->dy); +#endif + + ld->tranlump = -1; // killough 4/11/98: no translucency by default + + ld->slopetype = !ld->dx ? ST_VERTICAL : !ld->dy ? ST_HORIZONTAL : + FixedDiv(ld->dy, ld->dx) > 0 ? ST_POSITIVE : 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; + } + + /* calculate sound origin of line to be its midpoint */ + //e6y: fix sound origin for large levels + // no need for comp_sound test, these are only used when comp_sound = 0 + ld->soundorg.x = ld->bbox[BOXLEFT] / 2 + ld->bbox[BOXRIGHT] / 2; + ld->soundorg.y = ld->bbox[BOXTOP] / 2 + ld->bbox[BOXBOTTOM] / 2; + + ld->iLineID=i; // proff 04/05/2000: needed for OpenGL + ld->sidenum[0] = LittleShort(mld->sidenum[0]); + ld->sidenum[1] = LittleShort(mld->sidenum[1]); + + { + /* cph 2006/09/30 - fix sidedef errors right away. + * cph 2002/07/20 - these errors are fatal if not fixed, so apply them + * in compatibility mode - a desync is better than a crash! */ + int j; + + for (j=0; j < 2; j++) + { + if (ld->sidenum[j] != NO_INDEX && ld->sidenum[j] >= numsides) { + ld->sidenum[j] = NO_INDEX; + lprintf(LO_WARN, "P_LoadLineDefs: linedef %d" + " has out-of-range sidedef number\n", i); + } + } + + // killough 11/98: fix common wad errors (missing sidedefs): + + if (ld->sidenum[0] == NO_INDEX) { + ld->sidenum[0] = 0; // Substitute dummy sidedef for missing right side + // cph - print a warning about the bug + lprintf(LO_WARN, "P_LoadLineDefs: linedef %d" + " missing first sidedef\n", i); + } + + if ((ld->sidenum[1] == NO_INDEX) && (ld->flags & ML_TWOSIDED)) { + // e6y + // ML_TWOSIDED flag shouldn't be cleared for compatibility purposes + // see CLNJ-506.LMP at https://dsdarchive.com/wads/challenj + MissedBackSideOverrun(ld); + if (!demo_compatibility || !EMULATE(OVERFLOW_MISSEDBACKSIDE)) + { + ld->flags &= ~ML_TWOSIDED; // Clear 2s flag for missing left side + } + + // cph - print a warning about the bug + lprintf(LO_WARN, "P_LoadLineDefs: linedef %d" + " has two-sided flag set, but no second sidedef\n", i); + } + } + + // killough 4/4/98: support special sidedef interpretation below + if (ld->sidenum[0] != NO_INDEX && ld->special) + sides[*ld->sidenum].special = ld->special; + } + + W_UnlockLumpNum(lump); // cph - release the lump +} + +// killough 4/4/98: delay using sidedefs until they are loaded +// killough 5/3/98: reformatted, cleaned up + +static void P_LoadLineDefs2(int lump) +{ + int i = numlines; + register line_t *ld = lines; + for (;i--;ld++) + { + ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be NO_INDEX here + ld->backsector = ld->sidenum[1]!=NO_INDEX ? sides[ld->sidenum[1]].sector : 0; + switch (ld->special) + { // killough 4/11/98: handle special types + int lump, j; + + case 260: // killough 4/11/98: translucent 2s textures + transparentpresent = true;//e6y + lump = sides[*ld->sidenum].special; // translucency from sidedef + if (!ld->tag) // if tag==0, + ld->tranlump = lump; // affect this linedef only + else + for (j=0;jtag) // affect all matching linedefs + lines[j].tranlump = lump; + break; + } + } +} + +// +// P_LoadSideDefs +// +// killough 4/4/98: split into two functions + +static void P_LoadSideDefs (int lump) +{ + numsides = W_LumpLength(lump) / sizeof(mapsidedef_t); + sides = calloc_IfSameLevel(sides, numsides, sizeof(side_t)); +} + +// killough 4/4/98: delay using texture names until +// after linedefs are loaded, to allow overloading. +// killough 5/3/98: reformatted, cleaned up + +static void P_LoadSideDefs2(int lump) +{ + const byte *data = W_CacheLumpNum(lump); // cph - const*, wad lump handling updated + int i; + + for (i=0; itextureoffset = LittleShort(msd->textureoffset)<rowoffset = LittleShort(msd->rowoffset)<sector); + if (sector_num >= numsectors) { + lprintf(LO_WARN,"P_LoadSideDefs2: sidedef %i has out-of-range sector num %u\n", i, sector_num); + sector_num = 0; + } + sd->sector = sec = §ors[sector_num]; + } + + // killough 4/4/98: allow sidedef texture names to be overloaded + // killough 4/11/98: refined to allow colormaps to work as wall + // textures if invalid as colormaps but valid as textures. + switch (sd->special) + { + case 242: // variable colormap via 242 linedef + sd->bottomtexture = + (sec->bottommap = R_ColormapNumForName(msd->bottomtexture)) < 0 ? + sec->bottommap = 0, R_TextureNumForName(msd->bottomtexture): 0 ; + sd->midtexture = + (sec->midmap = R_ColormapNumForName(msd->midtexture)) < 0 ? + sec->midmap = 0, R_TextureNumForName(msd->midtexture) : 0 ; + sd->toptexture = + (sec->topmap = R_ColormapNumForName(msd->toptexture)) < 0 ? + sec->topmap = 0, R_TextureNumForName(msd->toptexture) : 0 ; + break; + + case 260: // killough 4/11/98: apply translucency to 2s normal texture + sd->midtexture = strncasecmp("TRANMAP", msd->midtexture, 8) ? + (sd->special = W_CheckNumForName(msd->midtexture)) < 0 || + W_LumpLength(sd->special) != 65536 ? + sd->special=0, R_TextureNumForName(msd->midtexture) : + (sd->special++, 0) : (sd->special=0); + sd->toptexture = R_TextureNumForName(msd->toptexture); + sd->bottomtexture = R_TextureNumForName(msd->bottomtexture); + break; + +#ifdef GL_DOOM + case 271: + case 272: + if (R_CheckTextureNumForName(msd->toptexture) == -1) + { + sd->skybox_index = R_BoxSkyboxNumForName(msd->toptexture); + } + // fallthrough +#endif + + default: // normal cases + sd->midtexture = R_SafeTextureNumForName(msd->midtexture, i); + sd->toptexture = R_SafeTextureNumForName(msd->toptexture, i); + sd->bottomtexture = R_SafeTextureNumForName(msd->bottomtexture, i); + break; + } + } + + W_UnlockLumpNum(lump); // cph - release the lump +} + +// +// jff 10/6/98 +// New code added to speed up calculation of internal blockmap +// Algorithm is order of nlines*(ncols+nrows) not nlines*ncols*nrows +// + +#define blkshift 7 /* places to shift rel position for cell num */ +#define blkmask ((1<0 + // jff 10/12/98 0 ok with + 1 in rows,cols + +typedef struct linelist_t // type used to list lines in each block +{ + long num; + struct linelist_t *next; +} linelist_t; + +// +// Subroutine to add a line number to a block list +// It simply returns if the line is already in the block +// + +static void AddBlockLine +( + linelist_t **lists, + int *count, + int *done, + int blockno, + long lineno +) +{ + linelist_t *l; + + if (done[blockno]) + return; + + l = malloc(sizeof(linelist_t)); + l->num = lineno; + l->next = lists[blockno]; + lists[blockno] = l; + count[blockno]++; + done[blockno] = 1; +} + +// +// Actually construct the blockmap lump from the level data +// +// This finds the intersection of each linedef with the column and +// row lines at the left and bottom of each blockmap cell. It then +// adds the line to all block lists touching the intersection. +// + +static void P_CreateBlockMap(void) +{ + int xorg,yorg; // blockmap origin (lower left) + int nrows,ncols; // blockmap dimensions + linelist_t **blocklists=NULL; // array of pointers to lists of lines + int *blockcount=NULL; // array of counters of line lists + int *blockdone=NULL; // array keeping track of blocks/line + int NBlocks; // number of cells = nrows*ncols + long linetotal=0; // total length of all blocklists + int i,j; + int map_minx=INT_MAX; // init for map limits search + int map_miny=INT_MAX; + int map_maxx=INT_MIN; + int map_maxy=INT_MIN; + + // scan for map limits, which the blockmap must enclose + + for (i=0;i map_maxx) + map_maxx = t; + if ((t=vertexes[i].y) < map_miny) + map_miny = t; + else if (t > map_maxy) + map_maxy = t; + } + map_minx >>= FRACBITS; // work in map coords, not fixed_t + map_maxx >>= FRACBITS; + map_miny >>= FRACBITS; + map_maxy >>= FRACBITS; + + // set up blockmap area to enclose level plus margin + + xorg = map_minx-blkmargin; + yorg = map_miny-blkmargin; + ncols = (map_maxx+blkmargin-xorg+1+blkmask)>>blkshift; //jff 10/12/98 + nrows = (map_maxy+blkmargin-yorg+1+blkmask)>>blkshift; //+1 needed for + NBlocks = ncols*nrows; //map exactly 1 cell + + // create the array of pointers on NBlocks to blocklists + // also create an array of linelist counts on NBlocks + // finally make an array in which we can mark blocks done per line + + // CPhipps - calloc's + blocklists = calloc(NBlocks,sizeof(linelist_t *)); + blockcount = calloc(NBlocks,sizeof(int)); + blockdone = malloc(NBlocks*sizeof(int)); + + // initialize each blocklist, and enter the trailing -1 in all blocklists + // note the linked list of lines grows backwards + + for (i=0;inum = -1; + blocklists[i]->next = NULL; + blockcount[i]++; + } + + // For each linedef in the wad, determine all blockmap blocks it touches, + // and add the linedef number to the blocklists for those blocks + + for (i=0;ix>>FRACBITS; // lines[i] map coords + int y1 = lines[i].v1->y>>FRACBITS; + int x2 = lines[i].v2->x>>FRACBITS; + int y2 = lines[i].v2->y>>FRACBITS; + int dx = x2-x1; + int dy = y2-y1; + int vert = !dx; // lines[i] slopetype + int horiz = !dy; + int spos = (dx^dy) > 0; + int sneg = (dx^dy) < 0; + int bx,by; // block cell coords + int minx = x1>x2? x2 : x1; // extremal lines[i] coords + int maxx = x1>x2? x1 : x2; + int miny = y1>y2? y2 : y1; + int maxy = y1>y2? y1 : y2; + + // no blocks done for this linedef yet + + memset(blockdone,0,NBlocks*sizeof(int)); + + // The line always belongs to the blocks containing its endpoints + + bx = (x1-xorg)>>blkshift; + by = (y1-yorg)>>blkshift; + AddBlockLine(blocklists,blockcount,blockdone,by*ncols+bx,i); + bx = (x2-xorg)>>blkshift; + by = (y2-yorg)>>blkshift; + AddBlockLine(blocklists,blockcount,blockdone,by*ncols+bx,i); + + + // For each column, see where the line along its left edge, which + // it contains, intersects the Linedef i. Add i to each corresponding + // blocklist. + + if (!vert) // don't interesect vertical lines with columns + { + for (j=0;j>blkshift; // block row number + int yp = (y-yorg)&blkmask; // y position within block + + if (yb<0 || yb>nrows-1) // outside blockmap, continue + continue; + + if (xmaxx) // line doesn't touch column + continue; + + // The cell that contains the intersection point is always added + + AddBlockLine(blocklists,blockcount,blockdone,ncols*yb+j,i); + + // if the intersection is at a corner it depends on the slope + // (and whether the line extends past the intersection) which + // blocks are hit + + if (yp==0) // intersection at a corner + { + if (sneg) // \ - blocks x,y-, x-,y + { + if (yb>0 && miny0 && minx0 && j>0 && minx0 && minx0 && minx>blkshift; // block column number + int xp = (x-xorg)&blkmask; // x position within block + + if (xb<0 || xb>ncols-1) // outside blockmap, continue + continue; + + if (ymaxy) // line doesn't touch row + continue; + + // The cell that contains the intersection point is always added + + AddBlockLine(blocklists,blockcount,blockdone,ncols*j+xb,i); + + // if the intersection is at a corner it depends on the slope + // (and whether the line extends past the intersection) which + // blocks are hit + + if (xp==0) // intersection at a corner + { + if (sneg) // \ - blocks x,y-, x-,y + { + if (j>0 && miny0 && minx0 && miny0 && j>0 && miny0 && minynext; + blockmaplump[offs++] = bl->num; + free(bl); + bl = tmp; + } + } + + // free all temporary storage + + free (blocklists); + free (blockcount); + free (blockdone); +} + +// jff 10/6/98 +// End new code added to speed up calculation of internal blockmap + +// +// P_VerifyBlockMap +// +// haleyjd 03/04/10: do verification on validity of blockmap. +// +static dboolean P_VerifyBlockMap(int count) +{ + int x, y; + int *maxoffs = blockmaplump + count; + + for(y = 0; y < bmapheight; y++) + { + for(x = 0; x < bmapwidth; x++) + { + int offset; + int *list, *tmplist; + int *blockoffset; + + offset = y * bmapwidth + x; + blockoffset = blockmaplump + offset + 4; + + // check that block offset is in bounds + if(blockoffset >= maxoffs) + { + lprintf(LO_ERROR, "P_VerifyBlockMap: block offset overflow\n"); + return false; + } + + offset = *blockoffset; + + // check that list offset is in bounds + if(offset < 4 || offset >= count) + { + lprintf(LO_ERROR, "P_VerifyBlockMap: list offset overflow\n"); + return false; + } + + list = blockmaplump + offset; + + // scan forward for a -1 terminator before maxoffs + for(tmplist = list; ; tmplist++) + { + // we have overflowed the lump? + if(tmplist >= maxoffs) + { + lprintf(LO_ERROR, "P_VerifyBlockMap: open blocklist\n"); + return false; + } + if(*tmplist == -1) // found -1 + break; + } + + // scan the list for out-of-range linedef indicies in list + for(tmplist = list; *tmplist != -1; tmplist++) + { + if(*tmplist < 0 || *tmplist >= numlines) + { + lprintf(LO_ERROR, "P_VerifyBlockMap: index >= numlines\n"); + return false; + } + } + } + } + + return true; +} + +// +// P_LoadBlockMap +// +// killough 3/1/98: substantially modified to work +// towards removing blockmap limit (a wad limitation) +// +// killough 3/30/98: Rewritten to remove blockmap limit, +// though current algorithm is brute-force and unoptimal. +// + +static void P_LoadBlockMap (int lump) +{ + long count; + + if (M_CheckParm("-blockmap") || W_LumpLength(lump)<8 || (count = W_LumpLength(lump)/2) >= 0x10000) //e6y + // COMPAT: MBF uses a different algorithm in P_CreateBlockMap() + P_CreateBlockMap(); + else + { + long i; + // cph - const*, wad lump handling updated + const short *wadblockmaplump = W_CacheLumpNum(lump); + blockmaplump = malloc_IfSameLevel(blockmaplump, sizeof(*blockmaplump) * count); + + // killough 3/1/98: Expand wad blockmap into larger internal one, + // by treating all offsets except -1 as unsigned and zero-extending + // them. This potentially doubles the size of blockmaps allowed, + // because Doom originally considered the offsets as always signed. + + blockmaplump[0] = LittleShort(wadblockmaplump[0]); + blockmaplump[1] = LittleShort(wadblockmaplump[1]); + blockmaplump[2] = (long)(LittleShort(wadblockmaplump[2])) & 0xffff; + blockmaplump[3] = (long)(LittleShort(wadblockmaplump[3])) & 0xffff; + + for (i=4 ; i 255 ? bmapwidth - 512 : -257); + blockmapyneg = (bmapheight > 255 ? bmapheight - 512 : -257); + if (blockmapxneg != -257 || blockmapyneg != -257) + { + lprintf(LO_WARN, + "P_LoadBlockMap: This map uses a large blockmap which may cause no-clipping bugs. " + "Toggle the \"Fix clipping problems in large levels\" option " + "in the \"Compatibility with common mapping errors\" menu in order to activate a fix. " + "That fix won't be applied during demo playback or recording.\n"); + } +} + +// +// P_LoadReject - load the reject table +// + +static void P_LoadReject(int lumpnum, int totallines) +{ + // dump any old cached reject lump, then cache the new one + if (rejectlump != -1) + W_UnlockLumpNum(rejectlump); + rejectlump = lumpnum + ML_REJECT; + rejectmatrix = W_CacheLumpNum(rejectlump); + + //e6y: check for overflow + RejectOverrun(rejectlump, &rejectmatrix, totallines); +} + +// +// P_GroupLines +// Builds sector line lists and subsector sector numbers. +// Finds block bounding boxes for sectors. +// +// killough 5/3/98: reformatted, cleaned up +// cph 18/8/99: rewritten to avoid O(numlines * numsectors) section +// It makes things more complicated, but saves seconds on big levels +// figgi 09/18/00 -- adapted for gl-nodes + +// cph - convenient sub-function +static void P_AddLineToSector(line_t* li, sector_t* sector) +{ + fixed_t *bbox = (void*)sector->blockbox; + + sector->lines[sector->linecount++] = li; + M_AddToBox (bbox, li->v1->x, li->v1->y); + M_AddToBox (bbox, li->v2->x, li->v2->y); +} + +// modified to return totallines (needed by P_LoadReject) +static int P_GroupLines (void) +{ + register line_t *li; + register sector_t *sector; + int i,j, total = numlines; + + // figgi + for (i=0 ; isidedef) + { + subsectors[i].sector = seg->sidedef->sector; + break; + } + seg++; + } + if(subsectors[i].sector == NULL) + I_Error("P_GroupLines: Subsector a part of no sector!\n"); + } + + // count number of lines in each sector + for (i=0,li=lines; ifrontsector->linecount++; + if (li->backsector && li->backsector != li->frontsector) + { + li->backsector->linecount++; + total++; + } + } + + { // allocate line tables for each sector + line_t **linebuffer = Z_Malloc(total*sizeof(line_t *), PU_LEVEL, 0); + // e6y: REJECT overrun emulation code + // moved to P_LoadReject + + for (i=0, sector = sectors; ilines = linebuffer; + linebuffer += sector->linecount; + sector->linecount = 0; + M_ClearBox(sector->blockbox); + } + } + + // Enter those lines + for (i=0,li=lines; ifrontsector); + if (li->backsector && li->backsector != li->frontsector) + P_AddLineToSector(li, li->backsector); + } + + for (i=0, sector = sectors; iblockbox; // cph - For convenience, so + // I can sue the old code unchanged + int block; + + sector->bbox[0] = sector->blockbox[0] >> FRACTOMAPBITS; + sector->bbox[1] = sector->blockbox[1] >> FRACTOMAPBITS; + sector->bbox[2] = sector->blockbox[2] >> FRACTOMAPBITS; + sector->bbox[3] = sector->blockbox[3] >> FRACTOMAPBITS; + + // set the degenmobj_t to the middle of the bounding box + if (default_comp[comp_sound]) + { + sector->soundorg.x = (bbox[BOXRIGHT]+bbox[BOXLEFT])/2; + sector->soundorg.y = (bbox[BOXTOP]+bbox[BOXBOTTOM])/2; + } + else + { + //e6y: fix sound origin for large levels + sector->soundorg.x = bbox[BOXRIGHT]/2+bbox[BOXLEFT]/2; + sector->soundorg.y = bbox[BOXTOP]/2+bbox[BOXBOTTOM]/2; + } + + // adjust bounding box to map blocks + block = P_GetSafeBlockY(bbox[BOXTOP]-bmaporgy+MAXRADIUS); + block = block >= bmapheight ? bmapheight-1 : block; + sector->blockbox[BOXTOP]=block; + + block = P_GetSafeBlockY(bbox[BOXBOTTOM]-bmaporgy-MAXRADIUS); + block = block < 0 ? 0 : block; + sector->blockbox[BOXBOTTOM]=block; + + block = P_GetSafeBlockX(bbox[BOXRIGHT]-bmaporgx+MAXRADIUS); + block = block >= bmapwidth ? bmapwidth-1 : block; + sector->blockbox[BOXRIGHT]=block; + + block = P_GetSafeBlockX(bbox[BOXLEFT]-bmaporgx-MAXRADIUS); + block = block < 0 ? 0 : block; + sector->blockbox[BOXLEFT]=block; + } + + return total; // this value is needed by the reject overrun emulation code +} + +// +// killough 10/98 +// +// Remove slime trails. +// +// Slime trails are inherent to Doom's coordinate system -- i.e. there is +// nothing that a node builder can do to prevent slime trails ALL of the time, +// because it's a product of the integer coodinate system, and just because +// two lines pass through exact integer coordinates, doesn't necessarily mean +// that they will intersect at integer coordinates. Thus we must allow for +// fractional coordinates if we are to be able to split segs with node lines, +// as a node builder must do when creating a BSP tree. +// +// A wad file does not allow fractional coordinates, so node builders are out +// of luck except that they can try to limit the number of splits (they might +// also be able to detect the degree of roundoff error and try to avoid splits +// with a high degree of roundoff error). But we can use fractional coordinates +// here, inside the engine. It's like the difference between square inches and +// square miles, in terms of granularity. +// +// For each vertex of every seg, check to see whether it's also a vertex of +// the linedef associated with the seg (i.e, it's an endpoint). If it's not +// an endpoint, and it wasn't already moved, move the vertex towards the +// linedef by projecting it using the law of cosines. Formula: +// +// 2 2 2 2 +// dx x0 + dy x1 + dx dy (y0 - y1) dy y0 + dx y1 + dx dy (x0 - x1) +// {---------------------------------, ---------------------------------} +// 2 2 2 2 +// dx + dy dx + dy +// +// (x0,y0) is the vertex being moved, and (x1,y1)-(x1+dx,y1+dy) is the +// reference linedef. +// +// Segs corresponding to orthogonal linedefs (exactly vertical or horizontal +// linedefs), which comprise at least half of all linedefs in most wads, don't +// need to be considered, because they almost never contribute to slime trails +// (because then any roundoff error is parallel to the linedef, which doesn't +// cause slime). Skipping simple orthogonal lines lets the code finish quicker. +// +// Please note: This section of code is not interchangable with TeamTNT's +// code which attempts to fix the same problem. +// +// Firelines (TM) is a Rezistered Trademark of MBF Productions +// + +static void P_RemoveSlimeTrails(void) // killough 10/98 +{ + byte *hit = calloc(1, numvertexes); // Hitlist for vertices + int i; + // Correction of desync on dv04-423.lmp/dv.wad + // http://www.doomworld.com/vb/showthread.php?s=&postid=627257#post627257 + int apply_for_real_vertexes = (compatibility_level>=lxdoom_1_compatibility || prboom_comp[PC_REMOVE_SLIME_TRAILS].state); + + for (i=0; idx && l->dy) // We can ignore orthogonal lines + { + vertex_t *v = segs[i].v1; + do + if (!hit[v - vertexes]) // If we haven't processed vertex + { + hit[v - vertexes] = 1; // Mark this vertex as processed + if (v != l->v1 && v != l->v2) // Exclude endpoints of linedefs + { // Project the vertex back onto the parent linedef + int_64_t dx2 = (l->dx >> FRACBITS) * (l->dx >> FRACBITS); + int_64_t dy2 = (l->dy >> FRACBITS) * (l->dy >> FRACBITS); + int_64_t dxy = (l->dx >> FRACBITS) * (l->dy >> FRACBITS); + int_64_t s = dx2 + dy2; + int x0 = v->x, y0 = v->y, x1 = l->v1->x, y1 = l->v1->y; + v->px = (int)((dx2 * x0 + dy2 * x1 + dxy * (y0 - y1)) / s); + v->py = (int)((dy2 * y0 + dx2 * y1 + dxy * (x0 - x1)) / s); + + // [crispy] wait a minute... moved more than 8 map units? + // maybe that's a linguortal then, back to the original coordinates + // http://www.doomworld.com/vb/doom-editing/74354-stupid-bsp-tricks/ + if (!apply_for_real_vertexes && (D_abs(v->px - v->x) > 8*FRACUNIT || D_abs(v->py - v->y) > 8*FRACUNIT)) + { + v->px = v->x; + v->py = v->y; + } + + if (apply_for_real_vertexes) + { + v->x = v->px; + v->y = v->py; + } + } + } // Obsfucated C contest entry: :) + while ((v != segs[i].v2) && (v = segs[i].v2)); + } + } + free(hit); +} + +static void R_CalcSegsLength(void) +{ + int i; + for (i=0; iv2->px - li->v1->px; + int_64_t dy = (int_64_t)li->v2->py - li->v1->py; + length = sqrt((double)dx*dx + (double)dy*dy); + li->length = (int_64_t)length; + // [crispy] re-calculate angle used for rendering + li->pangle = R_PointToAngleEx2(li->v1->px, li->v1->py, li->v2->px, li->v2->py); + } +} + +// +// P_CheckLumpsForSameSource +// +// Are these lumps in the same wad file? +// + +dboolean P_CheckLumpsForSameSource(int lump1, int lump2) +{ + int wad1_index, wad2_index; + wadfile_info_t *wad1, *wad2; + + if (((unsigned)lump1 >= (unsigned)numlumps) || ((unsigned)lump2 >= (unsigned)numlumps)) + return false; + + wad1 = lumpinfo[lump1].wadfile; + wad2 = lumpinfo[lump2].wadfile; + + if (!wad1 || !wad2) + return false; + + wad1_index = (int)(wad1 - wadfiles); + wad2_index = (int)(wad2 - wadfiles); + + if (wad1_index != wad2_index) + return false; + + if ((wad1_index < 0) || ((size_t)wad1_index >= numwadfiles)) + return false; + + if ((wad2_index < 0) || ((size_t)wad2_index >= numwadfiles)) + return false; + + return true; +} + +// +// P_CheckLevelFormat +// +// Checking for presence of necessary lumps +// + +void P_CheckLevelWadStructure(const char *mapname) +{ + int i, lumpnum; + + static const char *ml_labels[] = { + "ML_LABEL", // A separator, name, ExMx or MAPxx + "ML_THINGS", // Monsters, items.. + "ML_LINEDEFS", // LineDefs, from editing + "ML_SIDEDEFS", // SideDefs, from editing + "ML_VERTEXES", // Vertices, edited and BSP splits generated + "ML_SEGS", // LineSegs, from LineDefs split by BSP + "ML_SSECTORS", // SubSectors, list of LineSegs + "ML_NODES", // BSP nodes + "ML_SECTORS", // Sectors, from editing + "ML_REJECT", // LUT, sector-sector visibility + "ML_BLOCKMAP", // LUT, motion clipping, walls/grid element + }; + + if (!mapname) + { + I_Error("P_SetupLevel: Wrong map name"); + } + + lumpnum = W_CheckNumForName(mapname); + + if (lumpnum < 0) + { + I_Error("P_SetupLevel: There is no %s map.", mapname); + } + + for (i = ML_THINGS + 1; i <= ML_SECTORS; i++) + { + if (!P_CheckLumpsForSameSource(lumpnum, lumpnum + i)) + { + I_Error("P_SetupLevel: Level wad structure is incomplete. There is no %s lump.", ml_labels[i]); + } + } + + // refuse to load Hexen-format maps, avoid segfaults + i = lumpnum + ML_BLOCKMAP + 1; + if (P_CheckLumpsForSameSource(lumpnum, i)) + { + if (!strncasecmp(lumpinfo[i].name, "BEHAVIOR", 8)) + { + I_Error("P_SetupLevel: %s: Hexen format not supported", mapname); + } + } +} + +void P_InitSubsectorsLines(void) +{ + int num, count; + + if (sslines_indexes) + { + free(sslines_indexes); + sslines_indexes = NULL; + } + + if (sslines) + { + free(sslines); + sslines = NULL; + } + + count = 0; + sslines_indexes = malloc((numsubsectors + 1) * sizeof(sslines_indexes[0])); + + for (num = 0; num < numsubsectors; num++) + { + seg_t *seg; + const seg_t *seg_last = segs + subsectors[num].firstline + subsectors[num].numlines; + + sslines_indexes[num] = count; + + for (seg = segs + subsectors[num].firstline; seg < seg_last; seg++) + { + if (!seg->linedef) continue; + seg->linedef->validcount = 0; + } + + for (seg = segs + subsectors[num].firstline; seg < seg_last; seg++) + { + if (!seg->linedef) continue; + + if (seg->linedef->validcount == 1) + continue; + + seg->linedef->validcount = 1; + count++; + } + } + + sslines_indexes[numsubsectors] = count; + + sslines = malloc(count * sizeof(sslines[0])); + count = 0; + + for (num = 0; num < numsubsectors; num++) + { + seg_t *seg; + const seg_t *seg_last = segs + subsectors[num].firstline + subsectors[num].numlines; + + for (seg = segs + subsectors[num].firstline; seg < seg_last; seg++) + { + if (!seg->linedef) continue; + seg->linedef->validcount = 0; + } + + for (seg = segs + subsectors[num].firstline; seg < seg_last; seg++) + { + ssline_t *ssline = &sslines[count]; + if (!seg->linedef) continue; + + if (seg->linedef->validcount == 1) + continue; + + seg->linedef->validcount = 1; + + ssline->seg = seg; + ssline->linedef = seg->linedef; + + ssline->x1 = seg->linedef->v1->x; + ssline->y1 = seg->linedef->v1->y; + ssline->x2 = seg->linedef->v2->x; + ssline->y2 = seg->linedef->v2->y; + ssline->bbox[0] = seg->linedef->bbox[0]; + ssline->bbox[1] = seg->linedef->bbox[1]; + ssline->bbox[2] = seg->linedef->bbox[2]; + ssline->bbox[3] = seg->linedef->bbox[3]; + + count++; + } + } + + for (num = 0; num < numlines; num++) + { + lines[num].validcount = 0; + } +} + +// +// P_SetupLevel +// +// killough 5/3/98: reformatted, cleaned up + +// [FG] current map lump number +int maplumpnum = -1; + +void P_SetupLevel(int episode, int map, int playermask, skill_t skill) +{ + int i; + char lumpname[9]; + int lumpnum; + + char gl_lumpname[9]; + int gl_lumpnum; + + //e6y + totallive = 0; + transparentpresent = false; + + R_StopAllInterpolations(); + + totallive = totalkills = totalitems = totalsecret = wminfo.maxfrags = 0; + wminfo.partime = 180; + + for (i=0; i 0) + P_LoadVertexes2 (lumpnum+ML_VERTEXES,gl_lumpnum+ML_GL_VERTS); + else + P_LoadVertexes (lumpnum+ML_VERTEXES); + P_LoadSectors (lumpnum+ML_SECTORS); + P_LoadSideDefs (lumpnum+ML_SIDEDEFS); + P_LoadLineDefs (lumpnum+ML_LINEDEFS); + P_LoadSideDefs2 (lumpnum+ML_SIDEDEFS); + P_LoadLineDefs2 (lumpnum+ML_LINEDEFS); + + // e6y: speedup of level reloading + // Do not reload BlockMap for same level, + // because in case of big level P_CreateBlockMap eats much time + // + // BlockMap should be reloaded after OVERFLOW_INTERCEPT, + // because bmapwidth/bmapheight/bmaporgx/bmaporgy can be overwritten + if (!samelevel || overflows[OVERFLOW_INTERCEPT].shit_happens) + { + P_LoadBlockMap (lumpnum+ML_BLOCKMAP); + } + else + { + memset(blocklinks, 0, bmapwidth*bmapheight*sizeof(*blocklinks)); + } + + if (nodesVersion > 0) + { + P_LoadSubsectors(gl_lumpnum + ML_GL_SSECT); + P_LoadNodes(gl_lumpnum + ML_GL_NODES); + P_LoadGLSegs(gl_lumpnum + ML_GL_SEGS); + } + else + { + int zdoom_nodes; + if ((zdoom_nodes = P_CheckForZDoomUncompressedNodes(lumpnum, gl_lumpnum))) + P_LoadZNodes(lumpnum + ML_NODES, 0, zdoom_nodes); + else if (P_CheckForDeePBSPv4Nodes(lumpnum, gl_lumpnum)) + { + P_LoadSubsectors_V4(lumpnum + ML_SSECTORS); + P_LoadNodes_V4(lumpnum + ML_NODES); + P_LoadSegs_V4(lumpnum + ML_SEGS); + } + else + { + P_LoadSubsectors(lumpnum + ML_SSECTORS); + P_LoadNodes(lumpnum + ML_NODES); + P_LoadSegs(lumpnum + ML_SEGS); + } + } + + if (!samelevel) + { + P_InitSubsectorsLines(); + } + +#ifdef GL_DOOM + map_subsectors = calloc_IfSameLevel(map_subsectors, + numsubsectors, sizeof(map_subsectors[0])); +#endif + + // reject loading and underflow padding separated out into new function + // P_GroupLines modified to return a number the underflow padding needs + P_LoadReject(lumpnum, P_GroupLines()); + + P_RemoveSlimeTrails(); // killough 10/98: remove slime trails from wad + + // should be after P_RemoveSlimeTrails, because it changes vertexes + R_CalcSegsLength(); + + // Note: you don't need to clear player queue slots -- + // a much simpler fix is in g_game.c -- killough 10/98 + + bodyqueslot = 0; + + /* cph - reset all multiplayer starts */ + memset(playerstarts,0,sizeof(playerstarts)); + deathmatch_p = deathmatchstarts; + for (i = 0; i < MAXPLAYERS; i++) + players[i].mo = NULL; + TracerClearStarts(); + + P_MapStart(); + + P_LoadThings(lumpnum+ML_THINGS); + + // if deathmatch, randomly spawn the active players + if (deathmatch) + { + for (i=0; iflags |= (MF_NOGRAVITY | MF_FLY); + } + + // killough 3/26/98: Spawn icon landings: + if (gamemode==commercial) + P_SpawnBrainTargets(); + + if (gamemode != shareware) + { + S_ParseMusInfo(lumpname); + } + + // clear special respawning que + iquehead = iquetail = 0; + + // set up world state + P_SpawnSpecials(); + + P_MapEnd(); + + // preload graphics + if (precache) + R_PrecacheLevel(); + + // [FG] current map lump number + maplumpnum = lumpnum; + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + // e6y + // Do not preprocess GL data during skipping, + // because it potentially will not be used. + // But preprocessing must be called immediately after stop of skipping. + if (!doSkip) + { + // proff 11/99: calculate all OpenGL specific tables etc. + gld_PreprocessLevel(); + } + } +#endif + //e6y + P_SyncWalkcam(true, true); + R_SmoothPlaying_Reset(NULL); +} + +// +// P_Init +// +void P_Init (void) +{ + P_InitSwitchList(); + P_InitPicAnims(); + R_InitSprites(sprnames); +} diff --git a/src/p_setup.h b/src/p_setup.h new file mode 100644 index 0000000..bd75296 --- /dev/null +++ b/src/p_setup.h @@ -0,0 +1,61 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Setup a game, startup stuff. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_SETUP__ +#define __P_SETUP__ + +#include "p_mobj.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +void P_SetupLevel(int episode, int map, int playermask, skill_t skill); +void P_Init(void); /* Called by startup code. */ + +extern const byte *rejectmatrix; /* for fast sight rejection - cph - const* */ + +/* killough 3/1/98: change blockmap from "short" to "long" offsets: */ +extern int *blockmaplump; /* offsets in blockmap are from here */ +extern int *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 */ + +// MAES: extensions to support 512x512 blockmaps. +extern int blockmapxneg; +extern int blockmapyneg; + +#endif diff --git a/src/p_sight.c b/src/p_sight.c new file mode 100644 index 0000000..e657f14 --- /dev/null +++ b/src/p_sight.c @@ -0,0 +1,940 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * LineOfSight/Visibility checks, uses REJECT Lookup Table. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "doomtype.h" +#include "r_main.h" +#include "p_map.h" +#include "p_maputl.h" +#include "p_setup.h" +#include "m_bbox.h" +#include "lprintf.h" +#include "g_overflow.h" +#include "e6y.h" //e6y + + +/* +============================================================================== + +P_CheckSight +This uses specialized forms of the maputils routines for optimized performance + +============================================================================== +*/ + +fixed_t sightzstart; // eye z of looker +fixed_t topslope, bottomslope; // slopes to top and bottom of target +int sightcounts[3]; + +CrossSubsectorFunc P_CrossSubsector; + +/* +============== += += PTR_SightTraverse += +============== +*/ + +dboolean PTR_SightTraverse(intercept_t *in) +{ + line_t *li; + fixed_t slope; + + li = in->d.line; + + // + // crosses a two sided line + // + P_LineOpening(li); + + if (openbottom >= opentop) // quick test for totally closed doors + return false; // stop + + if (li->frontsector->floorheight != li->backsector->floorheight) + { + slope = FixedDiv(openbottom - sightzstart , in->frac); + if (slope > bottomslope) + bottomslope = slope; + } + + if (li->frontsector->ceilingheight != li->backsector->ceilingheight) + { + slope = FixedDiv(opentop - sightzstart, in->frac); + if (slope < topslope) + topslope = slope; + } + + if (topslope <= bottomslope) + return false; // stop + + return true; // keep going +} + + + +/* +================== += += P_SightBlockLinesIterator += +=================== +*/ + +dboolean P_SightBlockLinesIterator(int x, int y) +{ + int offset; + int *list; + line_t *ld; + int s1, s2; + divline_t dl; + + 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; + + s1 = P_PointOnDivlineSide(ld->v1->x, ld->v1->y, &trace); + s2 = P_PointOnDivlineSide(ld->v2->x, ld->v2->y, &trace); + if (s1 == s2) + continue; // line isn't crossed + P_MakeDivline (ld, &dl); + s1 = P_PointOnDivlineSide(trace.x, trace.y, &dl); + s2 = P_PointOnDivlineSide(trace.x+trace.dx, trace.y+trace.dy, &dl); + if (s1 == s2) + continue; // line isn't crossed + + // try to early out the check + if (!ld->backsector) + return false; // stop checking + + check_intercept(); // killough + + // store the line for later intersection testing + intercept_p->d.line = ld; + intercept_p++; + + } + + return true; // everything was checked +} + +/* +==================== += += P_SightTraverseIntercepts += += Returns true if the traverser function returns true for all lines +==================== +*/ + +dboolean P_SightTraverseIntercepts(void) +{ + int count; + fixed_t dist; + intercept_t *scan, *in; + divline_t dl; + + count = intercept_p - intercepts; + // + // calculate intercept distance + // + for (scan = intercepts; scand.line, &dl); + scan->frac = P_InterceptVector(&trace, &dl); + } + + // + // go through in order + // + in = 0; // shut up compiler warning + + while (count--) + { + dist = INT_MAX; + for (scan = intercepts ; scanfrac < dist) + { + dist = scan->frac; + in = scan; + } + + if (!PTR_SightTraverse(in)) + return false; // don't bother going farther + in->frac = INT_MAX; + } + + return true; // everything was traversed +} + + + +/* +================== += += P_SightPathTraverse += += 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 +================== +*/ + +dboolean P_SightPathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) +{ + fixed_t xt1,yt1,xt2,yt2; + fixed_t xstep,ystep; + fixed_t partial; + fixed_t xintercept, yintercept; + int mapx, mapy, mapxstep, mapystep; + int count; + + 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 = P_GetSafeBlockX(x1); + yt1 = P_GetSafeBlockY(y1); + + x2 -= bmaporgx; + y2 -= bmaporgy; + xt2 = P_GetSafeBlockX(x2); + yt2 = P_GetSafeBlockY(y2); + + // points should never be out of bounds, but check once instead of + // each block + if (xt1<0 || yt1<0 || xt1>=bmapwidth || yt1>=bmapheight + || xt2<0 || yt2<0 || xt2>=bmapwidth || yt2>=bmapheight) + return false; + + if (xt2 > xt1) + { + mapxstep = 1; + partial = FRACUNIT - ((x1>>MAPBTOFRAC)&(FRACUNIT-1)); + ystep = FixedDiv (y2-y1,abs(x2-x1)); + } + else if (xt2 < xt1) + { + mapxstep = -1; + partial = (x1>>MAPBTOFRAC)&(FRACUNIT-1); + ystep = FixedDiv (y2-y1,abs(x2-x1)); + } + else + { + mapxstep = 0; + partial = FRACUNIT; + ystep = 256*FRACUNIT; + } + yintercept = (y1>>MAPBTOFRAC) + FixedMul (partial, ystep); + + + if (yt2 > yt1) + { + mapystep = 1; + partial = FRACUNIT - ((y1>>MAPBTOFRAC)&(FRACUNIT-1)); + xstep = FixedDiv (x2-x1,abs(y2-y1)); + } + else if (yt2 < yt1) + { + mapystep = -1; + partial = (y1>>MAPBTOFRAC)&(FRACUNIT-1); + xstep = FixedDiv (x2-x1,abs(y2-y1)); + } + else + { + mapystep = 0; + partial = FRACUNIT; + xstep = 256*FRACUNIT; + } + xintercept = (x1>>MAPBTOFRAC) + FixedMul (partial, xstep); + + + // + // step through map blocks + // Count is present to prevent a round off error from skipping the break + mapx = xt1; + mapy = yt1; + + + for (count = 0; count < 64; count++) + { + if (!P_SightBlockLinesIterator(mapx, mapy)) + { + sightcounts[1]++; + 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; + } + + } + + + // + // couldn't early out, so go through the sorted list + // + sightcounts[2]++; + + return P_SightTraverseIntercepts(); +} + + + +/* +===================== += += P_CheckSight += += Returns true if a straight line between t1 and t2 is unobstructed += look from eyes of t1 to any part of t2 += +===================== +*/ + +dboolean P_CheckSight_12(mobj_t *t1, mobj_t *t2) +{ + int s1, s2; + int pnum, bytenum, bitnum; + + // + // check for trivial rejection + // + s1 = (t1->subsector->sector->iSectorID); + s2 = (t2->subsector->sector->iSectorID); + pnum = s1*numsectors + s2; + bytenum = pnum>>3; + bitnum = 1 << (pnum&7); + + if (rejectmatrix[bytenum]&bitnum) + { + sightcounts[0]++; + return false; // can't possibly be connected + } + + // + // check precisely + // + sightzstart = t1->z + t1->height - (t1->height>>2); + topslope = (t2->z+t2->height) - sightzstart; + bottomslope = (t2->z) - sightzstart; + + return P_SightPathTraverse (t1->x, t1->y, t2->x, t2->y); +} + +// +// P_CheckSight +// +// killough 4/19/98: +// Convert LOS info to struct for reentrancy and efficiency of data locality + +typedef struct { + fixed_t sightzstart, t2x, t2y; // eye z of looker + divline_t strace; // from t1 to t2 + fixed_t topslope, bottomslope; // slopes to top and bottom of target + fixed_t bbox[4]; + fixed_t maxz,minz; // cph - z optimisations for 2sided lines +} los_t; + +static los_t los; // cph - made static + +// +// P_DivlineSide +// Returns side 0 (front), 1 (back), or 2 (on). +// +// killough 4/19/98: made static, cleaned up + +INLINE static int P_DivlineSide(fixed_t x, fixed_t y, const divline_t *node) +{ + fixed_t left, right; + return + !node->dx ? x == node->x ? 2 : x <= node->x ? node->dy > 0 : node->dy < 0 : + !node->dy ? ( compatibility_level < prboom_4_compatibility ? x : y) == node->y ? 2 : y <= node->y ? node->dx < 0 : node->dx > 0 : + (right = ((y - node->y) >> FRACBITS) * (node->dx >> FRACBITS)) < + (left = ((x - node->x) >> FRACBITS) * (node->dy >> FRACBITS)) ? 0 : + right == left ? 2 : 1; +} + +INLINE static int P_DivlineCrossed(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, divline_t *node) +{ +#if 1 + return (P_DivlineSide(x1, y1, node) == P_DivlineSide(x2, y2, node)); +#else + if (!node->dx) + { + if (x1 == node->x) + return (x2 == node->x); + if (x2 == node->x) + return (x1 == node->x); + if (x1 < node->x) + return (x2 < node->x); + return (x2 > node->x); + } + + if (!node->dy) + { + fixed_t _y1, _y2; + if (compatibility_level < prboom_4_compatibility) + { + _y1 = x1; + _y2 = x2; + } + else + { + _y1 = y1; + _y2 = y2; + } + //if ((compatibility_level < prboom_4_compatibility ? x1 : y1) == node->y) + // return (x2 == node->y); + if (_y1 == node->y) + return (_y2 == node->y); + if (_y2 == node->y) + return (_y1 == node->y); + if (y1 <= node->y) + return (y2 < node->y); + return (y2 > node->y); + } + + { + fixed_t node_dx = (node->dx >> FRACBITS); + fixed_t node_dy = (node->dy >> FRACBITS); + + fixed_t left1 = node_dy * ((x1 - node->x) >> FRACBITS); + fixed_t right1 = ((y1 - node->y) >> FRACBITS) * node_dx; + fixed_t left2 = node_dy * ((x2 - node->x) >> FRACBITS); + fixed_t right2 = ((y2 - node->y) >> FRACBITS) * node_dx; + + if (right1 < left1) + return (right2 < left2); + + if (left1 == right1) + return (left2 == right2); + + return (right2 > left2); + } +#endif +} + + +// +// P_CrossSubsector +// Returns true +// if strace crosses the given subsector successfully. +// +// killough 4/19/98: made static and cleaned up + +dboolean P_CrossSubsector_PrBoom(int num) +{ + ssline_t *ssline = &sslines[sslines_indexes[num]]; + const ssline_t *ssline_last = &sslines[sslines_indexes[num + 1]]; + fixed_t opentop = 0, openbottom = 0; + const sector_t *front = NULL, *back = NULL; + +#ifdef RANGECHECK + if (num >= numsubsectors) + I_Error("P_CrossSubsector: ss %i with numss = %i", num, numsubsectors); +#endif + + // check lines + for (; ssline < ssline_last; ssline++) + { + divline_t divl; + + /* OPTIMIZE: killough 4/20/98: Added quick bounding-box rejection test + * cph - this is causing demo desyncs on original Doom demos. + * Who knows why. Exclude test for those. + */ + if (ssline->bbox[BOXLEFT ] > los.bbox[BOXRIGHT ] || + ssline->bbox[BOXRIGHT ] < los.bbox[BOXLEFT ] || + ssline->bbox[BOXBOTTOM] > los.bbox[BOXTOP ] || + ssline->bbox[BOXTOP] < los.bbox[BOXBOTTOM]) + { + ssline->linedef->validcount = validcount; + continue; + } + + // Forget this line if it doesn't cross the line of sight + if (P_DivlineCrossed(ssline->x1, ssline->y1, ssline->x2, ssline->y2, &los.strace)) + { + ssline->linedef->validcount = validcount; + continue; + } + + divl.dx = ssline->x2 - (divl.x = ssline->x1); + divl.dy = ssline->y2 - (divl.y = ssline->y1); + + // line isn't crossed? + if (P_DivlineCrossed(los.strace.x, los.strace.y, los.t2x, los.t2y, &divl)) + { + ssline->linedef->validcount = validcount; + continue; + } + + // allready checked other side? + if (ssline->linedef->validcount == validcount) + continue; + + ssline->linedef->validcount = validcount; + + // cph - do what we can before forced to check intersection + if (ssline->linedef->flags & ML_TWOSIDED) + { + // crosses a two sided line + front = ssline->seg->frontsector; + back = ssline->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 + opentop = MIN(front->ceilingheight, back->ceilingheight); + + // because of floor height differences + openbottom = MAX(front->floorheight, back->floorheight); + + // cph - reject if does not intrude in the z-space of the possible LOS + if ((opentop >= los.maxz) && (openbottom <= los.minz)) + continue; + } + + // cph - if bottom >= top or top < minz or bottom > maxz then it must be + // solid wrt this LOS + if (!(ssline->linedef->flags & ML_TWOSIDED) || (openbottom >= opentop) || + (prboom_comp[PC_FORCE_LXDOOM_DEMO_COMPATIBILITY].state ? + (opentop <= los.minz) || (openbottom >= los.maxz) : + (opentop < los.minz) || (openbottom > los.maxz))) + return false; + + { // crosses a two sided line + /* cph 2006/07/15 - oops, we missed this in 2.4.0 & .1; + * use P_InterceptVector2 for those compat levels only. */ + fixed_t frac = (compatibility_level == prboom_5_compatibility || compatibility_level == prboom_6_compatibility) ? + P_InterceptVector2(&los.strace, &divl) : + P_InterceptVector(&los.strace, &divl); + + if (front->floorheight != back->floorheight) + { + fixed_t slope = FixedDiv(openbottom - los.sightzstart, frac); + if (slope > los.bottomslope) + los.bottomslope = slope; + } + + if (front->ceilingheight != back->ceilingheight) + { + fixed_t slope = FixedDiv(opentop - los.sightzstart, frac); + if (slope < los.topslope) + los.topslope = slope; + } + + if (los.topslope <= los.bottomslope) + return false; // stop + } + } + // passed the subsector ok + return true; +} + +dboolean P_CrossSubsector_Doom(int num) +{ + ssline_t *ssline = &sslines[sslines_indexes[num]]; + const ssline_t *ssline_last = &sslines[sslines_indexes[num + 1]]; + +#ifdef RANGECHECK + if (num >= numsubsectors) + I_Error("P_CrossSubsector: ss %i with numss = %i", num, numsubsectors); +#endif + + for (; ssline < ssline_last; ssline++) + { + divline_t divl; + fixed_t opentop, openbottom; + const sector_t *front, *back; + fixed_t frac; + + // line isn't crossed? + if (P_DivlineCrossed(ssline->x1, ssline->y1, ssline->x2, ssline->y2, &los.strace)) + { + ssline->linedef->validcount = validcount; + continue; + } + + divl.dx = ssline->x2 - (divl.x = ssline->x1); + divl.dy = ssline->y2 - (divl.y = ssline->y1); + + // line isn't crossed? + if (P_DivlineCrossed(los.strace.x, los.strace.y, los.t2x, los.t2y, &divl)) + { + ssline->linedef->validcount = validcount; + continue; + } + + // allready checked other side? + if (ssline->linedef->validcount == validcount) + continue; + + ssline->linedef->validcount = validcount; + + // stop because it is not two sided anyway + if (!(ssline->linedef->flags & ML_TWOSIDED)) + return false; + + // crosses a two sided line + front = ssline->seg->frontsector; + back = ssline->seg->backsector; + + // missed back side on two-sided lines. + if (!back) + { + back = GetSectorAtNullAddress(); + } + + // no wall to block sight with? + if (front->floorheight == back->floorheight + && front->ceilingheight == back->ceilingheight) + continue; + + // possible occluder + // because of ceiling height differences + opentop = MIN(front->ceilingheight, back->ceilingheight); + + // because of floor height differences + openbottom = MAX(front->floorheight, back->floorheight); + + // quick test for totally closed doors + if (openbottom >= opentop) + return false; // stop + + frac = P_InterceptVector2(&los.strace, &divl); + + if (front->floorheight != back->floorheight) + { + fixed_t slope = FixedDiv(openbottom - los.sightzstart, frac); + if (slope > los.bottomslope) + los.bottomslope = slope; + } + + if (front->ceilingheight != back->ceilingheight) + { + fixed_t slope = FixedDiv(opentop - los.sightzstart, frac); + if (slope < los.topslope) + los.topslope = slope; + } + + if (los.topslope <= los.bottomslope) + return false; // stop + } + // passed the subsector ok + return true; +} + +dboolean P_CrossSubsector_Boom(int num) +{ + ssline_t *ssline = &sslines[sslines_indexes[num]]; + const ssline_t *ssline_last = &sslines[sslines_indexes[num + 1]]; + +#ifdef RANGECHECK + if (num >= numsubsectors) + I_Error("P_CrossSubsector: ss %i with numss = %i", num, numsubsectors); +#endif + + for (; ssline < ssline_last; ssline++) + { + divline_t divl; + fixed_t opentop, openbottom; + const sector_t *front, *back; + fixed_t frac; + + // OPTIMIZE: killough 4/20/98: Added quick bounding-box rejection test + + if (ssline->bbox[BOXLEFT ] > los.bbox[BOXRIGHT ] || + ssline->bbox[BOXRIGHT ] < los.bbox[BOXLEFT ] || + ssline->bbox[BOXBOTTOM] > los.bbox[BOXTOP ] || + ssline->bbox[BOXTOP] < los.bbox[BOXBOTTOM]) + { + ssline->linedef->validcount = validcount; + continue; + } + + // line isn't crossed? + if (P_DivlineCrossed(ssline->x1, ssline->y1, ssline->x2, ssline->y2, &los.strace)) + { + ssline->linedef->validcount = validcount; + continue; + } + + divl.dx = ssline->x2 - (divl.x = ssline->x1); + divl.dy = ssline->y2 - (divl.y = ssline->y1); + + // line isn't crossed? + if (P_DivlineCrossed(los.strace.x, los.strace.y, los.t2x, los.t2y, &divl)) + { + ssline->linedef->validcount = validcount; + continue; + } + + // allready checked other side? + if (ssline->linedef->validcount == validcount) + continue; + + ssline->linedef->validcount = validcount; + + // stop because it is not two sided anyway + if (!(ssline->linedef->flags & ML_TWOSIDED)) + return false; + + // crosses a two sided line + front = ssline->seg->frontsector; + back = ssline->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 + opentop = MIN(front->ceilingheight, back->ceilingheight); + + // because of floor height differences + openbottom = MAX(front->floorheight, back->floorheight); + + // quick test for totally closed doors + if (openbottom >= opentop) + return false; // stop + + frac = P_InterceptVector2(&los.strace, &divl); + + if (front->floorheight != back->floorheight) + { + fixed_t slope = FixedDiv(openbottom - los.sightzstart, frac); + if (slope > los.bottomslope) + los.bottomslope = slope; + } + + if (front->ceilingheight != back->ceilingheight) + { + fixed_t slope = FixedDiv(opentop - los.sightzstart, frac); + if (slope < los.topslope) + los.topslope = slope; + } + + if (los.topslope <= los.bottomslope) + return false; // stop + } + // passed the subsector ok + return true; +} + +// +// P_CrossBSPNode +// Returns true +// if strace crosses the given node successfully. +// +// killough 4/20/98: rewritten to remove tail recursion, clean up, and optimize +// cph - Made to use R_PointOnSide instead of P_DivlineSide, since the latter +// could return 2 which was ambigous, and the former is +// better optimised; also removes two casts :-) + +static dboolean P_CrossBSPNode_LxDoom(int bspnum) +{ + while (!(bspnum & NF_SUBSECTOR)) + { + register const node_t *bsp = nodes + bspnum; + int side,side2; + side = R_PointOnSide(los.strace.x, los.strace.y, bsp); + side2 = R_PointOnSide(los.t2x, los.t2y, bsp); + if (side == side2) + bspnum = bsp->children[side]; // doesn't touch the other side + else // the partition plane is crossed here + if (!P_CrossBSPNode_LxDoom(bsp->children[side])) + return 0; // cross the starting side + else + bspnum = bsp->children[side^1]; // cross the ending side + } + return P_CrossSubsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR); +} + +static dboolean P_CrossBSPNode_PrBoom(int bspnum) +{ + while (!(bspnum & NF_SUBSECTOR)) + { + register const node_t *bsp = nodes + bspnum; + int side,side2; + side = P_DivlineSide(los.strace.x,los.strace.y,(const divline_t *)bsp)&1; + side2= P_DivlineSide(los.t2x, los.t2y, (const divline_t *) bsp); + if (side == side2) + bspnum = bsp->children[side]; // doesn't touch the other side + else // the partition plane is crossed here + if (!P_CrossBSPNode_PrBoom(bsp->children[side])) + return 0; // cross the starting side + else + bspnum = bsp->children[side^1]; // cross the ending side + } + return P_CrossSubsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR); +} + +/* proff - Moved the compatibility check outside the functions + * this gives a slight speedup + */ +static dboolean P_CrossBSPNode(int bspnum) +{ + /* cph - LxDoom used some R_* funcs here */ + if (compatibility_level == lxdoom_1_compatibility || prboom_comp[PC_FORCE_LXDOOM_DEMO_COMPATIBILITY].state) + return P_CrossBSPNode_LxDoom(bspnum); + else + return P_CrossBSPNode_PrBoom(bspnum); +} + +// +// P_CheckSight +// Returns true +// if a straight line between t1 and t2 is unobstructed. +// Uses REJECT. +// +// killough 4/20/98: cleaned up, made to use new LOS struct + +dboolean P_CheckSight(mobj_t *t1, mobj_t *t2) +{ + const sector_t *s1, *s2; + int pnum; + + if (compatibility_level == doom_12_compatibility) + { + return P_CheckSight_12(t1, t2); + } + + s1 = t1->subsector->sector; + s2 = t2->subsector->sector; + pnum = (s1->iSectorID)*numsectors + (s2->iSectorID); + + // First check for trivial rejection. + // Determine subsector entries in REJECT table. + // + // Check in REJECT table. + + if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected + return false; + + // killough 4/19/98: make fake floors and ceilings block monster view + + if ((s1->heightsec != -1 && + ((t1->z + t1->height <= sectors[s1->heightsec].floorheight && + t2->z >= sectors[s1->heightsec].floorheight) || + (t1->z >= sectors[s1->heightsec].ceilingheight && + t2->z + t1->height <= sectors[s1->heightsec].ceilingheight))) + || + (s2->heightsec != -1 && + ((t2->z + t2->height <= sectors[s2->heightsec].floorheight && + t1->z >= sectors[s2->heightsec].floorheight) || + (t2->z >= sectors[s2->heightsec].ceilingheight && + t1->z + t2->height <= sectors[s2->heightsec].ceilingheight)))) + return false; + + /* killough 11/98: shortcut for melee situations + * same subsector? obviously visible + * cph - compatibility optioned for demo sync, cf HR06-UV.LMP */ + if ((t1->subsector == t2->subsector) && + (compatibility_level >= mbf_compatibility)) + return true; + + // An unobstructed LOS is possible. + // Now look from eyes of t1 to any part of t2. + + validcount++; + + los.topslope = (los.bottomslope = t2->z - (los.sightzstart = + t1->z + t1->height - + (t1->height>>2))) + t2->height; + los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x); + los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y); + + if (t1->x > t2->x) + los.bbox[BOXRIGHT] = t1->x, los.bbox[BOXLEFT] = t2->x; + else + los.bbox[BOXRIGHT] = t2->x, los.bbox[BOXLEFT] = t1->x; + + if (t1->y > t2->y) + los.bbox[BOXTOP] = t1->y, los.bbox[BOXBOTTOM] = t2->y; + else + los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y; + + /* cph - calculate min and max z of the potential line of sight + * For old demos, we disable this optimisation by setting them to + * the extremes */ + if (compatibility_level == lxdoom_1_compatibility || prboom_comp[PC_FORCE_LXDOOM_DEMO_COMPATIBILITY].state) + { + if (los.sightzstart < t2->z) { + los.maxz = t2->z + t2->height; los.minz = los.sightzstart; + } else if (los.sightzstart > t2->z + t2->height) { + los.maxz = los.sightzstart; los.minz = t2->z; + } else { + los.maxz = t2->z + t2->height; los.minz = t2->z; + } + } + else + { + los.maxz = INT_MAX; los.minz = INT_MIN; + } + + // the head node is the last node output + return P_CrossBSPNode(numnodes-1); +} diff --git a/src/p_spec.c b/src/p_spec.c new file mode 100644 index 0000000..160b99a --- /dev/null +++ b/src/p_spec.c @@ -0,0 +1,3525 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * -Loads and initializes texture and flat animation sequences + * -Implements utility functions for all linedef/sector special handlers + * -Dispatches walkover and gun line triggers + * -Initializes and implements special sector types + * -Implements donut linedef triggers + * -Initializes and implements BOOM linedef triggers for + * Scrollers/Conveyors + * Friction + * Wind/Current + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "p_spec.h" +#include "p_tick.h" +#include "p_setup.h" +#include "m_random.h" +#include "d_englsh.h" +#include "m_argv.h" +#include "w_wad.h" +#include "r_main.h" +#include "p_maputl.h" +#include "p_map.h" +#include "g_game.h" +#include "p_inter.h" +#include "s_sound.h" +#include "sounds.h" +#include "i_sound.h" +#include "m_bbox.h" // phares 3/20/98 +#include "d_deh.h" +#include "r_plane.h" +#include "hu_stuff.h" +#include "lprintf.h" +#include "e6y.h"//e6y + +// +// source animation definition +// +// +#ifdef _MSC_VER // proff: This is the same as __attribute__ ((packed)) in GNUC +#pragma pack(push) +#pragma pack(1) +#endif //_MSC_VER + +#if defined(__MWERKS__) +#pragma options align=packed +#endif + +typedef struct +{ + signed char istexture; //jff 3/23/98 make char for comparison // cph - make signed + char endname[9]; // if false, it is a flat + char startname[9]; + int speed; +} PACKEDATTR animdef_t; //jff 3/23/98 pack to read from memory + +#if defined(__MWERKS__) +#pragma options align=reset +#endif + +#ifdef _MSC_VER +#pragma pack(pop) +#endif //_MSC_VER + +#define MAXANIMS 32 // no longer a strict limit -- killough + +static anim_t* lastanim; +static anim_t* anims; // new structure w/o limits -- killough +static size_t maxanims; + +// killough 3/7/98: Initialize generalized scrolling +static void P_SpawnScrollers(void); + +static void P_SpawnFriction(void); // phares 3/16/98 +static void P_SpawnPushers(void); // phares 3/20/98 + +//e6y +void MarkAnimatedTextures(void) +{ +#ifdef GL_DOOM + anim_t* anim; + + anim_textures = calloc(numtextures, sizeof(TAnimItemParam)); + anim_flats = calloc(numflats, sizeof(TAnimItemParam)); + + for (anim = anims ; anim < lastanim ; anim++) + { + int i; + for (i = 0; i < anim->numpics ; i++) + { + if (anim->istexture) + { + anim_textures[anim->basepic + i].anim = anim; + anim_textures[anim->basepic + i].index = i + 1; + } + else + { + anim_flats[anim->basepic + i].anim = anim; + anim_flats[anim->basepic + i].index = i + 1; + } + } + } +#endif // GL_DOOM +} + +// +// P_InitPicAnims +// +// Load the table of animation definitions, checking for existence of +// the start and end of each frame. If the start doesn't exist the sequence +// is skipped, if the last doesn't exist, BOOM exits. +// +// Wall/Flat animation sequences, defined by name of first and last frame, +// The full animation sequence is given using all lumps between the start +// and end entry, in the order found in the WAD file. +// +// This routine modified to read its data from a predefined lump or +// PWAD lump called ANIMATED rather than a static table in this module to +// allow wad designers to insert or modify animation sequences. +// +// Lump format is an array of byte packed animdef_t structures, terminated +// by a structure with istexture == -1. The lump can be generated from a +// text source file using SWANTBLS.EXE, distributed with the BOOM utils. +// The standard list of switches and animations is contained in the example +// source text file DEFSWANI.DAT also in the BOOM util distribution. +// +// +void P_InitPicAnims (void) +{ + int i; + const animdef_t *animdefs; //jff 3/23/98 pointer to animation lump + int lump = W_GetNumForName("ANIMATED"); // cph - new wad lump handling + // Init animation + + //jff 3/23/98 read from predefined or wad lump instead of table + animdefs = (const animdef_t *)W_CacheLumpNum(lump); + + lastanim = anims; + for (i=0 ; animdefs[i].istexture != -1 ; i++) + { + // 1/11/98 killough -- removed limit by array-doubling + if (lastanim >= anims + maxanims) + { + size_t newmax = maxanims ? maxanims*2 : MAXANIMS; + anims = realloc(anims, newmax*sizeof(*anims)); // killough + lastanim = anims + maxanims; + maxanims = newmax; + } + + if (animdefs[i].istexture) + { + // different episode ? + if (R_CheckTextureNumForName(animdefs[i].startname) == -1) + continue; + + lastanim->picnum = R_TextureNumForName (animdefs[i].endname); + lastanim->basepic = R_TextureNumForName (animdefs[i].startname); + } + else + { + if ((W_CheckNumForName)(animdefs[i].startname, ns_flats) == -1) // killough 4/17/98 + continue; + + lastanim->picnum = R_FlatNumForName (animdefs[i].endname); + lastanim->basepic = R_FlatNumForName (animdefs[i].startname); + } + + lastanim->istexture = animdefs[i].istexture; + lastanim->numpics = lastanim->picnum - lastanim->basepic + 1; + + if (lastanim->numpics < 2) + I_Error ("P_InitPicAnims: bad cycle from %s to %s", + animdefs[i].startname, + animdefs[i].endname); + + lastanim->speed = LittleLong(animdefs[i].speed); // killough 5/5/98: add LONG() + lastanim++; + } + W_UnlockLumpNum(lump); + MarkAnimatedTextures();//e6y +} + +/////////////////////////////////////////////////////////////// +// +// Linedef and Sector Special Implementation Utility Functions +// +/////////////////////////////////////////////////////////////// + +// +// getSide() +// +// Will return a side_t* +// given the number of the current sector, +// the line number, and the side (0/1) that you want. +// +// Note: if side=1 is specified, it must exist or results undefined +// +side_t* getSide +( int currentSector, + int line, + int side ) +{ + return &sides[ (sectors[currentSector].lines[line])->sidenum[side] ]; +} + + +// +// getSector() +// +// Will return a sector_t* +// given the number of the current sector, +// the line number and the side (0/1) that you want. +// +// Note: if side=1 is specified, it must exist or results undefined +// +sector_t* getSector +( int currentSector, + int line, + int side ) +{ + return sides[ (sectors[currentSector].lines[line])->sidenum[side] ].sector; +} + + +// +// twoSided() +// +// Given the sector number and the line number, +// it will tell you whether the line is two-sided or not. +// +// modified to return actual two-sidedness rather than presence +// of 2S flag unless compatibility optioned +// +int twoSided +( int sector, + int line ) +{ + //jff 1/26/98 return what is actually needed, whether the line + //has two sidedefs, rather than whether the 2S flag is set + + return comp[comp_model] ? + (sectors[sector].lines[line])->flags & ML_TWOSIDED + : + (sectors[sector].lines[line])->sidenum[1] != NO_INDEX; +} + + +// +// getNextSector() +// +// Return sector_t * of sector next to current across line. +// +// Note: returns NULL if not two-sided line, or both sides refer to sector +// +sector_t* getNextSector +( line_t* line, + sector_t* sec ) +{ + //jff 1/26/98 check unneeded since line->backsector already + //returns NULL if the line is not two sided, and does so from + //the actual two-sidedness of the line, rather than its 2S flag + + if (comp[comp_model]) + { + if (!(line->flags & ML_TWOSIDED)) + return NULL; + } + + if (line->frontsector == sec) { + if (comp[comp_model] || line->backsector!=sec) + return line->backsector; //jff 5/3/98 don't retn sec unless compatibility + else // fixes an intra-sector line breaking functions + return NULL; // like floor->highest floor + } + return line->frontsector; +} + + +// +// P_FindLowestFloorSurrounding() +// +// Returns the fixed point value of the lowest floor height +// in the sector passed or its surrounding sectors. +// +fixed_t P_FindLowestFloorSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t floor = sec->floorheight; + + for (i=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + + if (!other) + continue; + + if (other->floorheight < floor) + floor = other->floorheight; + } + return floor; +} + + +// +// P_FindHighestFloorSurrounding() +// +// Passed a sector, returns the fixed point value of the largest +// floor height in the surrounding sectors, not including that passed +// +// NOTE: if no surrounding sector exists -32000*FRACUINT is returned +// if compatibility then -500*FRACUNIT is the smallest return possible +// +fixed_t P_FindHighestFloorSurrounding(sector_t *sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t floor = -500*FRACUNIT; + + //jff 1/26/98 Fix initial value for floor to not act differently + //in sections of wad that are below -500 units + if (!comp[comp_model]) /* jff 3/12/98 avoid ovf */ + floor = -32000*FRACUNIT; // in height calculations + + for (i=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + + if (!other) + continue; + + if (other->floorheight > floor) + floor = other->floorheight; + } + return floor; +} + + +// +// P_FindNextHighestFloor() +// +// Passed a sector and a floor height, returns the fixed point value +// of the smallest floor height in a surrounding sector larger than +// the floor height passed. If no such height exists the floorheight +// passed is returned. +// +// Rewritten by Lee Killough to avoid fixed array and to be faster +// +fixed_t P_FindNextHighestFloor(sector_t *sec, int currentheight) +{ + sector_t *other; + int i; + + // e6y + // Original P_FindNextHighestFloor() is restored for demo_compatibility + // Adapted for prboom's complevels + if (demo_compatibility && !prboom_comp[PC_FORCE_BOOM_FINDNEXTHIGHESTFLOOR].state) + { + int h; + int min; + static int MAX_ADJOINING_SECTORS = 0; + static fixed_t *heightlist = NULL; + static int heightlist_size = 0; + line_t* check; + fixed_t height = currentheight; + static fixed_t last_height_0 = 0; + + // 20 adjoining sectors max! + if (!MAX_ADJOINING_SECTORS) + MAX_ADJOINING_SECTORS = M_CheckParm("-doom95") ? 500 : 20; + + if (sec->linecount > heightlist_size) + { + do + { + heightlist_size = heightlist_size ? heightlist_size * 2 : 128; + } while (sec->linecount > heightlist_size); + heightlist = realloc(heightlist, heightlist_size * sizeof(heightlist[0])); + } + + for (i=0, h=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + + if (!other) + continue; + + if (other->floorheight > height) + { + // e6y + // Emulation of stack overflow. + // 20: overflow affects nothing - just a luck; + // 21: can be emulated; + // 22..26: overflow affects saved registers - unpredictable behaviour, can crash; + // 27: overflow affects return address - crash with high probability; + if (compatibility_level < dosdoom_compatibility && h >= MAX_ADJOINING_SECTORS) + { + lprintf(LO_WARN, "P_FindNextHighestFloor: Overflow of heightlist[%d] array is detected.\n", MAX_ADJOINING_SECTORS); + lprintf(LO_WARN, " Sector %d, line %d, heightlist index %d: ", sec->iSectorID, sec->lines[i]->iLineID, h); + + if (h == MAX_ADJOINING_SECTORS + 1) + height = other->floorheight; + + if (h <= MAX_ADJOINING_SECTORS + 1) + lprintf(LO_WARN, "successfully emulated.\n"); + else if (h <= MAX_ADJOINING_SECTORS + 6) + lprintf(LO_WARN, "cannot be emulated - unpredictable behaviour.\n"); + else + lprintf(LO_WARN, "cannot be emulated - crash with high probability.\n"); + } + heightlist[h++] = other->floorheight; + } + + // Check for overflow. Warning. + if ( compatibility_level >= dosdoom_compatibility && h >= MAX_ADJOINING_SECTORS ) + { + lprintf( LO_WARN, "Sector with more than 20 adjoining sectors\n" ); + break; + } + } + + // Find lowest height in list + if (!h) + { + // cph - my guess at doom v1.2 - 1.4beta compatibility here. + // If there are no higher neighbouring sectors, Heretic just returned + // heightlist[0] (local variable), i.e. noise off the stack. 0 is right for + // RETURN01 E1M2, so let's take that. + // + // SmileTheory's response: + // It's not *quite* random stack noise. If this function is called + // as part of a loop, heightlist will be at the same location as in + // the previous call. Doing it this way fixes 1_ON_1.WAD. + return (compatibility_level < doom_1666_compatibility ? last_height_0 : currentheight); + } + + last_height_0 = heightlist[0]; + min = heightlist[0]; + + // Range checking? + for (i = 1;i < h;i++) + { + if (heightlist[i] < min) + min = heightlist[i]; + } + + return min; + } + + + for (i=0 ;i < sec->linecount ; i++) + if ((other = getNextSector(sec->lines[i],sec)) && + other->floorheight > currentheight) + { + int height = other->floorheight; + while (++i < sec->linecount) + if ((other = getNextSector(sec->lines[i],sec)) && + other->floorheight < height && + other->floorheight > currentheight) + height = other->floorheight; + return height; + } + /* cph - my guess at doom v1.2 - 1.4beta compatibility here. + * If there are no higher neighbouring sectors, Heretic just returned + * heightlist[0] (local variable), i.e. noise off the stack. 0 is right for + * RETURN01 E1M2, so let's take that. */ + return (compatibility_level < doom_1666_compatibility ? 0 : currentheight); +} + + +// +// P_FindNextLowestFloor() +// +// Passed a sector and a floor height, returns the fixed point value +// of the largest floor height in a surrounding sector smaller than +// the floor height passed. If no such height exists the floorheight +// passed is returned. +// +// jff 02/03/98 Twiddled Lee's P_FindNextHighestFloor to make this +// +fixed_t P_FindNextLowestFloor(sector_t *sec, int currentheight) +{ + sector_t *other; + int i; + + for (i=0 ;i < sec->linecount ; i++) + if ((other = getNextSector(sec->lines[i],sec)) && + other->floorheight < currentheight) + { + int height = other->floorheight; + while (++i < sec->linecount) + if ((other = getNextSector(sec->lines[i],sec)) && + other->floorheight > height && + other->floorheight < currentheight) + height = other->floorheight; + return height; + } + return currentheight; +} + + +// +// P_FindNextLowestCeiling() +// +// Passed a sector and a ceiling height, returns the fixed point value +// of the largest ceiling height in a surrounding sector smaller than +// the ceiling height passed. If no such height exists the ceiling height +// passed is returned. +// +// jff 02/03/98 Twiddled Lee's P_FindNextHighestFloor to make this +// +fixed_t P_FindNextLowestCeiling(sector_t *sec, int currentheight) +{ + sector_t *other; + int i; + + for (i=0 ;i < sec->linecount ; i++) + if ((other = getNextSector(sec->lines[i],sec)) && + other->ceilingheight < currentheight) + { + int height = other->ceilingheight; + while (++i < sec->linecount) + if ((other = getNextSector(sec->lines[i],sec)) && + other->ceilingheight > height && + other->ceilingheight < currentheight) + height = other->ceilingheight; + return height; + } + return currentheight; +} + + +// +// P_FindNextHighestCeiling() +// +// Passed a sector and a ceiling height, returns the fixed point value +// of the smallest ceiling height in a surrounding sector larger than +// the ceiling height passed. If no such height exists the ceiling height +// passed is returned. +// +// jff 02/03/98 Twiddled Lee's P_FindNextHighestFloor to make this +// +fixed_t P_FindNextHighestCeiling(sector_t *sec, int currentheight) +{ + sector_t *other; + int i; + + for (i=0 ;i < sec->linecount ; i++) + if ((other = getNextSector(sec->lines[i],sec)) && + other->ceilingheight > currentheight) + { + int height = other->ceilingheight; + while (++i < sec->linecount) + if ((other = getNextSector(sec->lines[i],sec)) && + other->ceilingheight < height && + other->ceilingheight > currentheight) + height = other->ceilingheight; + return height; + } + return currentheight; +} + + +// +// P_FindLowestCeilingSurrounding() +// +// Passed a sector, returns the fixed point value of the smallest +// ceiling height in the surrounding sectors, not including that passed +// +// NOTE: if no surrounding sector exists 32000*FRACUINT is returned +// but if compatibility then INT_MAX is the return +// +fixed_t P_FindLowestCeilingSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t height = INT_MAX; + + /* jff 3/12/98 avoid ovf in height calculations */ + if (!comp[comp_model]) height = 32000*FRACUNIT; + + for (i=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + + if (!other) + continue; + + if (other->ceilingheight < height) + height = other->ceilingheight; + } + return height; +} + + +// +// P_FindHighestCeilingSurrounding() +// +// Passed a sector, returns the fixed point value of the largest +// ceiling height in the surrounding sectors, not including that passed +// +// NOTE: if no surrounding sector exists -32000*FRACUINT is returned +// but if compatibility then 0 is the smallest return possible +// +fixed_t P_FindHighestCeilingSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t height = 0; + + /* jff 1/26/98 Fix initial value for floor to not act differently + * in sections of wad that are below 0 units + * jff 3/12/98 avoid ovf in height calculations */ + if (!comp[comp_model]) height = -32000*FRACUNIT; + + for (i=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + + if (!other) + continue; + + if (other->ceilingheight > height) + height = other->ceilingheight; + } + return height; +} + + +// +// P_FindShortestTextureAround() +// +// Passed a sector number, returns the shortest lower texture on a +// linedef bounding the sector. +// +// Note: If no lower texture exists 32000*FRACUNIT is returned. +// but if compatibility then INT_MAX is returned +// +// jff 02/03/98 Add routine to find shortest lower texture +// +fixed_t P_FindShortestTextureAround(int secnum) +{ + int minsize = INT_MAX; + side_t* side; + int i; + sector_t *sec = §ors[secnum]; + + if (!comp[comp_model]) + minsize = 32000<linecount; i++) + { + if (twoSided(secnum, i)) + { + side = getSide(secnum,i,0); + // COMPAT: MBF compares >= 0 here + if (side->bottomtexture > 0) //jff 8/14/98 texture 0 is a placeholder + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + side = getSide(secnum,i,1); + // COMPAT: MBF compares >= 0 here + if (side->bottomtexture > 0) //jff 8/14/98 texture 0 is a placeholder + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + } + } + return minsize; +} + + +// +// P_FindShortestUpperAround() +// +// Passed a sector number, returns the shortest upper texture on a +// linedef bounding the sector. +// +// Note: If no upper texture exists 32000*FRACUNIT is returned. +// but if compatibility then INT_MAX is returned +// +// jff 03/20/98 Add routine to find shortest upper texture +// +fixed_t P_FindShortestUpperAround(int secnum) +{ + int minsize = INT_MAX; + side_t* side; + int i; + sector_t *sec = §ors[secnum]; + + if (!comp[comp_model]) + minsize = 32000<linecount; i++) + { + if (twoSided(secnum, i)) + { + side = getSide(secnum,i,0); + // COMPAT: MBF compares >= 0 here + if (side->toptexture > 0) //jff 8/14/98 texture 0 is a placeholder + if (textureheight[side->toptexture] < minsize) + minsize = textureheight[side->toptexture]; + side = getSide(secnum,i,1); + // COMPAT: MBF compares >= 0 here + if (side->toptexture > 0) //jff 8/14/98 texture 0 is a placeholder + if (textureheight[side->toptexture] < minsize) + minsize = textureheight[side->toptexture]; + } + } + return minsize; +} + + +// +// P_FindModelFloorSector() +// +// Passed a floor height and a sector number, return a pointer to a +// a sector with that floor height across the lowest numbered two sided +// line surrounding the sector. +// +// Note: If no sector at that height bounds the sector passed, return NULL +// +// jff 02/03/98 Add routine to find numeric model floor +// around a sector specified by sector number +// jff 3/14/98 change first parameter to plain height to allow call +// from routine not using floormove_t +// +sector_t *P_FindModelFloorSector(fixed_t floordestheight,int secnum) +{ + int i; + sector_t *sec=NULL; + int linecount; + + sec = §ors[secnum]; //jff 3/2/98 woops! better do this + //jff 5/23/98 don't disturb sec->linecount while searching + // but allow early exit in old demos + linecount = sec->linecount; + for (i = 0; i < (demo_compatibility && sec->linecountlinecount : linecount); i++) + { + if ( twoSided(secnum, i) ) + { + if (getSide(secnum,i,0)->sector->iSectorID == secnum) + sec = getSector(secnum,i,1); + else + sec = getSector(secnum,i,0); + + if (sec->floorheight == floordestheight) + return sec; + } + } + return NULL; +} + + +// +// P_FindModelCeilingSector() +// +// Passed a ceiling height and a sector number, return a pointer to a +// a sector with that ceiling height across the lowest numbered two sided +// line surrounding the sector. +// +// Note: If no sector at that height bounds the sector passed, return NULL +// +// jff 02/03/98 Add routine to find numeric model ceiling +// around a sector specified by sector number +// used only from generalized ceiling types +// jff 3/14/98 change first parameter to plain height to allow call +// from routine not using ceiling_t +// +sector_t *P_FindModelCeilingSector(fixed_t ceildestheight,int secnum) +{ + int i; + sector_t *sec=NULL; + int linecount; + + sec = §ors[secnum]; //jff 3/2/98 woops! better do this + //jff 5/23/98 don't disturb sec->linecount while searching + // but allow early exit in old demos + linecount = sec->linecount; + for (i = 0; i < (demo_compatibility && sec->linecountlinecount : linecount); i++) + { + if ( twoSided(secnum, i) ) + { + if (getSide(secnum,i,0)->sector->iSectorID == secnum) + sec = getSector(secnum,i,1); + else + sec = getSector(secnum,i,0); + + if (sec->ceilingheight == ceildestheight) + return sec; + } + } + return NULL; +} + +// +// RETURN NEXT SECTOR # THAT LINE TAG REFERS TO +// + +// Find the next sector with the same tag as a linedef. +// Rewritten by Lee Killough to use chained hashing to improve speed + +int P_FindSectorFromLineTag(const line_t *line, int start) +{ + start = start >= 0 ? sectors[start].nexttag : + sectors[(unsigned) line->tag % (unsigned) numsectors].firsttag; + while (start >= 0 && sectors[start].tag != line->tag) + start = sectors[start].nexttag; + return start; +} + +// killough 4/16/98: Same thing, only for linedefs + +int P_FindLineFromLineTag(const line_t *line, int start) +{ + start = start >= 0 ? lines[start].nexttag : + lines[(unsigned) line->tag % (unsigned) numlines].firsttag; + while (start >= 0 && lines[start].tag != line->tag) + start = lines[start].nexttag; + return start; +} + +// Hash the sector tags across the sectors and linedefs. +static void P_InitTagLists(void) +{ + register int i; + + for (i=numsectors; --i>=0; ) // Initially make all slots empty. + sectors[i].firsttag = -1; + for (i=numsectors; --i>=0; ) // Proceed from last to first sector + { // so that lower sectors appear first + int j = (unsigned) sectors[i].tag % (unsigned) numsectors; // Hash func + sectors[i].nexttag = sectors[j].firsttag; // Prepend sector to chain + sectors[j].firsttag = i; + } + + // killough 4/17/98: same thing, only for linedefs + + for (i=numlines; --i>=0; ) // Initially make all slots empty. + lines[i].firsttag = -1; + for (i=numlines; --i>=0; ) // Proceed from last to first linedef + { // so that lower linedefs appear first + int j = (unsigned) lines[i].tag % (unsigned) numlines; // Hash func + lines[i].nexttag = lines[j].firsttag; // Prepend linedef to chain + lines[j].firsttag = i; + } +} + +// +// P_FindMinSurroundingLight() +// +// Passed a sector and a light level, returns the smallest light level +// in a surrounding sector less than that passed. If no smaller light +// level exists, the light level passed is returned. +// +int P_FindMinSurroundingLight +( sector_t* sector, + int max ) +{ + int i; + int min; + line_t* line; + sector_t* check; + + min = max; + for (i=0 ; i < sector->linecount ; i++) + { + line = sector->lines[i]; + check = getNextSector(line,sector); + + if (!check) + continue; + + if (check->lightlevel < min) + min = check->lightlevel; + } + return min; +} + + +// +// P_CanUnlockGenDoor() +// +// Passed a generalized locked door linedef and a player, returns whether +// the player has the keys necessary to unlock that door. +// +// Note: The linedef passed MUST be a generalized locked door type +// or results are undefined. +// +// jff 02/05/98 routine added to test for unlockability of +// generalized locked doors +// +dboolean P_CanUnlockGenDoor +( line_t* line, + player_t* player) +{ + // does this line special distinguish between skulls and keys? + int skulliscard = (line->special & LockedNKeys)>>LockedNKeysShift; + + // determine for each case of lock type if player's keys are adequate + switch((line->special & LockedKey)>>LockedKeyShift) + { + case AnyKey: + if + ( + !player->cards[it_redcard] && + !player->cards[it_redskull] && + !player->cards[it_bluecard] && + !player->cards[it_blueskull] && + !player->cards[it_yellowcard] && + !player->cards[it_yellowskull] + ) + { + player->message = s_PD_ANY; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case RCard: + if + ( + !player->cards[it_redcard] && + (!skulliscard || !player->cards[it_redskull]) + ) + { + player->message = skulliscard? s_PD_REDK : s_PD_REDC; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case BCard: + if + ( + !player->cards[it_bluecard] && + (!skulliscard || !player->cards[it_blueskull]) + ) + { + player->message = skulliscard? s_PD_BLUEK : s_PD_BLUEC; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case YCard: + if + ( + !player->cards[it_yellowcard] && + (!skulliscard || !player->cards[it_yellowskull]) + ) + { + player->message = skulliscard? s_PD_YELLOWK : s_PD_YELLOWC; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case RSkull: + if + ( + !player->cards[it_redskull] && + (!skulliscard || !player->cards[it_redcard]) + ) + { + player->message = skulliscard? s_PD_REDK : s_PD_REDS; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case BSkull: + if + ( + !player->cards[it_blueskull] && + (!skulliscard || !player->cards[it_bluecard]) + ) + { + player->message = skulliscard? s_PD_BLUEK : s_PD_BLUES; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case YSkull: + if + ( + !player->cards[it_yellowskull] && + (!skulliscard || !player->cards[it_yellowcard]) + ) + { + player->message = skulliscard? s_PD_YELLOWK : s_PD_YELLOWS; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case AllKeys: + if + ( + !skulliscard && + ( + !player->cards[it_redcard] || + !player->cards[it_redskull] || + !player->cards[it_bluecard] || + !player->cards[it_blueskull] || + !player->cards[it_yellowcard] || + !player->cards[it_yellowskull] + ) + ) + { + player->message = s_PD_ALL6; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + if + ( + skulliscard && + ( + (!player->cards[it_redcard] && + !player->cards[it_redskull]) || + (!player->cards[it_bluecard] && + !player->cards[it_blueskull]) || + // e6y + // Compatibility with buggy MBF behavior when 3-key door works with only 2 keys + // There is no more desync on 10sector.wad\ts27-137.lmp + // http://www.doomworld.com/tas/ts27-137.zip + (!player->cards[it_yellowcard] && + (compatibility_level == mbf_compatibility && + !prboom_comp[PC_FORCE_CORRECT_CODE_FOR_3_KEYS_DOORS_IN_MBF].state ? + player->cards[it_yellowskull] : + !player->cards[it_yellowskull])) + ) + ) + { + player->message = s_PD_ALL3; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + } + return true; +} + + +// +// P_SectorActive() +// +// Passed a linedef special class (floor, ceiling, lighting) and a sector +// returns whether the sector is already busy with a linedef special of the +// same class. If old demo compatibility true, all linedef special classes +// are the same. +// +// jff 2/23/98 added to prevent old demos from +// succeeding in starting multiple specials on one sector +// +dboolean PUREFUNC P_SectorActive(special_e t, const sector_t *sec) +{ + if (demo_compatibility) // return whether any thinker is active + return sec->floordata != NULL || sec->ceilingdata != NULL || sec->lightingdata != NULL; + else + switch (t) // return whether thinker of same type is active + { + case floor_special: + return sec->floordata != NULL; + case ceiling_special: + return sec->ceilingdata != NULL; + case lighting_special: + return sec->lightingdata != NULL; + } + return true; // don't know which special, must be active, shouldn't be here +} + + +// +// P_CheckTag() +// +// Passed a line, returns true if the tag is non-zero or the line special +// allows no tag without harm. If compatibility, all linedef specials are +// allowed to have zero tag. +// +// Note: Only line specials activated by walkover, pushing, or shooting are +// checked by this routine. +// +// jff 2/27/98 Added to check for zero tag allowed for regular special types +// +int P_CheckTag(line_t *line) +{ + /* tag not zero, allowed, or + * killough 11/98: compatibility option */ + if (comp[comp_zerotags] || line->tag || comperr(comperr_zerotag))//e6y + return 1; + + switch(line->special) + { + case 1: // Manual door specials + case 26: + case 27: + case 28: + case 31: + case 32: + case 33: + case 34: + case 117: + case 118: + + case 139: // Lighting specials + case 170: + case 79: + case 35: + case 138: + case 171: + case 81: + case 13: + case 192: + case 169: + case 80: + case 12: + case 194: + case 173: + case 157: + case 104: + case 193: + case 172: + case 156: + case 17: + + case 195: // Thing teleporters + case 174: + case 97: + case 39: + case 126: + case 125: + case 210: + case 209: + case 208: + case 207: + + case 11: // Exits + case 52: + case 197: + case 51: + case 124: + case 198: + + case 48: // Scrolling walls + case 85: + return 1; // zero tag allowed + + default: + break; + } + return 0; // zero tag not allowed +} + + +// +// P_IsSecret() +// +// Passed a sector, returns if the sector secret type is still active, i.e. +// secret type is set and the secret has not yet been obtained. +// +// jff 3/14/98 added to simplify checks for whether sector is secret +// in automap and other places +// +dboolean PUREFUNC P_IsSecret(const sector_t *sec) +{ + return (sec->special==9 || (sec->special&SECRET_MASK)); +} + + +// +// P_WasSecret() +// +// Passed a sector, returns if the sector secret type is was active, i.e. +// secret type was set and the secret has been obtained already. +// +// jff 3/14/98 added to simplify checks for whether sector is secret +// in automap and other places +// +dboolean PUREFUNC P_WasSecret(const sector_t *sec) +{ + return (sec->oldspecial==9 || (sec->oldspecial&SECRET_MASK)); +} + + +////////////////////////////////////////////////////////////////////////// +// +// Events +// +// Events are operations triggered by using, crossing, +// or shooting special lines, or by timed thinkers. +// +///////////////////////////////////////////////////////////////////////// + +// +// P_CrossSpecialLine - Walkover Trigger Dispatcher +// +// Called every time a thing origin is about +// to cross a line with a non 0 special, whether a walkover type or not. +// +// jff 02/12/98 all W1 lines were fixed to check the result from the EV_ +// function before clearing the special. This avoids losing the function +// of the line, should the sector already be active when the line is +// crossed. Change is qualified by demo_compatibility. +// +// CPhipps - take a line_t pointer instead of a line number, as in MBF +void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing, dboolean bossaction) +{ + int ok; + + // Things that should never trigger lines + // + // e6y: Improved support for Doom v1.2 + if (compatibility_level == doom_12_compatibility) + { + if (line->special > 98 && line->special != 104) + { + return; + } + } + else + { + if (!thing->player && !bossaction) + { + // Things that should NOT trigger specials... + switch(thing->type) + { + case MT_ROCKET: + case MT_PLASMA: + case MT_BFG: + case MT_TROOPSHOT: + case MT_HEADSHOT: + case MT_BRUISERSHOT: + return; + break; + + default: break; + } + } + } + + //jff 02/04/98 add check here for generalized lindef types + if (!demo_compatibility) // generalized types not recognized if old demo + { + // pointer to line function is NULL by default, set non-null if + // line special is walkover generalized linedef type + int (*linefunc)(line_t *line)=NULL; + + // check each range of generalized linedefs + if ((unsigned)line->special >= GenEnd) + { + // Out of range for GenFloors + } + else if ((unsigned)line->special >= GenFloorBase) + { + if (!thing->player && !bossaction) + if ((line->special & FloorChange) || !(line->special & FloorModel)) + return; // FloorModel is "Allow Monsters" if FloorChange is 0 + if (!comperr(comperr_zerotag) && !line->tag) //e6y //jff 2/27/98 all walk generalized types require tag + return; + linefunc = EV_DoGenFloor; + } + else if ((unsigned)line->special >= GenCeilingBase) + { + if (!thing->player && !bossaction) + if ((line->special & CeilingChange) || !(line->special & CeilingModel)) + return; // CeilingModel is "Allow Monsters" if CeilingChange is 0 + if (!comperr(comperr_zerotag) && !line->tag) //e6y //jff 2/27/98 all walk generalized types require tag + return; + linefunc = EV_DoGenCeiling; + } + else if ((unsigned)line->special >= GenDoorBase) + { + if (!thing->player && !bossaction) + { + if (!(line->special & DoorMonster)) + return; // monsters disallowed from this door + if (line->flags & ML_SECRET) // they can't open secret doors either + return; + } + if (!comperr(comperr_zerotag) && !line->tag) //e6y //3/2/98 move outside the monster check + return; + linefunc = EV_DoGenDoor; + } + else if ((unsigned)line->special >= GenLockedBase) + { + if (!thing->player || bossaction) // boss actions can't handle locked doors + return; // monsters disallowed from unlocking doors + if (((line->special&TriggerType)==WalkOnce) || ((line->special&TriggerType)==WalkMany)) + { //jff 4/1/98 check for being a walk type before reporting door type + if (!P_CanUnlockGenDoor(line,thing->player)) + return; + } + else + return; + linefunc = EV_DoGenLockedDoor; + } + else if ((unsigned)line->special >= GenLiftBase) + { + if (!thing->player && !bossaction) + if (!(line->special & LiftMonster)) + return; // monsters disallowed + if (!comperr(comperr_zerotag) && !line->tag) //e6y //jff 2/27/98 all walk generalized types require tag + return; + linefunc = EV_DoGenLift; + } + else if ((unsigned)line->special >= GenStairsBase) + { + if (!thing->player && !bossaction) + if (!(line->special & StairMonster)) + return; // monsters disallowed + if (!comperr(comperr_zerotag) && !line->tag) //e6y //jff 2/27/98 all walk generalized types require tag + return; + linefunc = EV_DoGenStairs; + } + + if (linefunc) // if it was a valid generalized type + switch((line->special & TriggerType) >> TriggerTypeShift) + { + case WalkOnce: + if (linefunc(line)) + line->special = 0; // clear special if a walk once type + return; + case WalkMany: + linefunc(line); + return; + default: // if not a walk type, do nothing here + return; + } + } + + if (!thing->player || bossaction) + { + ok = 0; + switch(line->special) + { + // teleporters are blocked for boss actions. + case 39: // teleport trigger + case 97: // teleport retrigger + case 125: // teleport monsteronly trigger + case 126: // teleport monsteronly retrigger + //jff 3/5/98 add ability of monsters etc. to use teleporters + case 208: //silent thing teleporters + case 207: + case 243: //silent line-line teleporter + case 244: //jff 3/6/98 make fit within DCK's 256 linedef types + case 262: //jff 4/14/98 add monster only + case 263: //jff 4/14/98 silent thing,line,line rev types + case 264: //jff 4/14/98 plus player/monster silent line + case 265: // reversed types + case 266: + case 267: + case 268: + case 269: + if (bossaction) return; + + case 4: // raise door + case 10: // plat down-wait-up-stay trigger + case 88: // plat down-wait-up-stay retrigger + ok = 1; + break; + } + if (!ok) + return; + } + + if (!P_CheckTag(line)) //jff 2/27/98 disallow zero tag on some types + return; + + // Dispatch on the line special value to the line's action routine + // If a once only function, and successful, clear the line special + + switch (line->special) + { + // Regular walk once triggers + + case 2: + // Open Door + if (EV_DoDoor(line,openDoor) || demo_compatibility) + line->special = 0; + break; + + case 3: + // Close Door + if (EV_DoDoor(line,closeDoor) || demo_compatibility) + line->special = 0; + break; + + case 4: + // Raise Door + if (EV_DoDoor(line,normal) || demo_compatibility) + line->special = 0; + break; + + case 5: + // Raise Floor + if (EV_DoFloor(line,raiseFloor) || demo_compatibility) + line->special = 0; + break; + + case 6: + // Fast Ceiling Crush & Raise + if (EV_DoCeiling(line,fastCrushAndRaise) || demo_compatibility) + line->special = 0; + break; + + case 8: + // Build Stairs + if (EV_BuildStairs(line,build8) || demo_compatibility) + line->special = 0; + break; + + case 10: + // PlatDownWaitUp + if (EV_DoPlat(line,downWaitUpStay,0) || demo_compatibility) + line->special = 0; + break; + + case 12: + // Light Turn On - brightest near + if (EV_LightTurnOn(line,0) || demo_compatibility) + line->special = 0; + break; + + case 13: + // Light Turn On 255 + if (EV_LightTurnOn(line,255) || demo_compatibility) + line->special = 0; + break; + + case 16: + // Close Door 30 + if (EV_DoDoor(line,close30ThenOpen) || demo_compatibility) + line->special = 0; + break; + + case 17: + // Start Light Strobing + if (EV_StartLightStrobing(line) || demo_compatibility) + line->special = 0; + break; + + case 19: + // Lower Floor + if (EV_DoFloor(line,lowerFloor) || demo_compatibility) + line->special = 0; + break; + + case 22: + // Raise floor to nearest height and change texture + if (EV_DoPlat(line,raiseToNearestAndChange,0) || demo_compatibility) + line->special = 0; + break; + + case 25: + // Ceiling Crush and Raise + if (EV_DoCeiling(line,crushAndRaise) || demo_compatibility) + line->special = 0; + break; + + case 30: + // Raise floor to shortest texture height + // on either side of lines. + if (EV_DoFloor(line,raiseToTexture) || demo_compatibility) + line->special = 0; + break; + + case 35: + // Lights Very Dark + if (EV_LightTurnOn(line,35) || demo_compatibility) + line->special = 0; + break; + + case 36: + // Lower Floor (TURBO) + if (EV_DoFloor(line,turboLower) || demo_compatibility) + line->special = 0; + break; + + case 37: + // LowerAndChange + if (EV_DoFloor(line,lowerAndChange) || demo_compatibility) + line->special = 0; + break; + + case 38: + // Lower Floor To Lowest + if (EV_DoFloor(line, lowerFloorToLowest) || demo_compatibility) + line->special = 0; + break; + + case 39: + // TELEPORT! //jff 02/09/98 fix using up with wrong side crossing + if (EV_Teleport(line, side, thing) || demo_compatibility) + line->special = 0; + break; + + case 40: + // RaiseCeilingLowerFloor + if (demo_compatibility) + { + EV_DoCeiling( line, raiseToHighest ); + EV_DoFloor( line, lowerFloorToLowest ); //jff 02/12/98 doesn't work + line->special = 0; + } + else + if (EV_DoCeiling(line, raiseToHighest)) + line->special = 0; + break; + + case 44: + // Ceiling Crush + if (EV_DoCeiling(line, lowerAndCrush) || demo_compatibility) + line->special = 0; + break; + + case 52: + // EXIT! + // killough 10/98: prevent zombies from exiting levels + if (bossaction || (!(thing->player && thing->player->health <= 0 && !comp[comp_zombie]))) + G_ExitLevel (); + break; + + case 53: + // Perpetual Platform Raise + if (EV_DoPlat(line,perpetualRaise,0) || demo_compatibility) + line->special = 0; + break; + + case 54: + // Platform Stop + if (EV_StopPlat(line) || demo_compatibility) + line->special = 0; + break; + + case 56: + // Raise Floor Crush + if (EV_DoFloor(line,raiseFloorCrush) || demo_compatibility) + line->special = 0; + break; + + case 57: + // Ceiling Crush Stop + if (EV_CeilingCrushStop(line) || demo_compatibility) + line->special = 0; + break; + + case 58: + // Raise Floor 24 + if (EV_DoFloor(line,raiseFloor24) || demo_compatibility) + line->special = 0; + break; + + case 59: + // Raise Floor 24 And Change + if (EV_DoFloor(line,raiseFloor24AndChange) || demo_compatibility) + line->special = 0; + break; + + case 100: + // Build Stairs Turbo 16 + if (EV_BuildStairs(line,turbo16) || demo_compatibility) + line->special = 0; + break; + + case 104: + // Turn lights off in sector(tag) + if (EV_TurnTagLightsOff(line) || demo_compatibility) + line->special = 0; + break; + + case 108: + // Blazing Door Raise (faster than TURBO!) + if (EV_DoDoor(line,blazeRaise) || demo_compatibility) + line->special = 0; + break; + + case 109: + // Blazing Door Open (faster than TURBO!) + if (EV_DoDoor (line,blazeOpen) || demo_compatibility) + line->special = 0; + break; + + case 110: + // Blazing Door Close (faster than TURBO!) + if (EV_DoDoor (line,blazeClose) || demo_compatibility) + line->special = 0; + break; + + case 119: + // Raise floor to nearest surr. floor + if (EV_DoFloor(line,raiseFloorToNearest) || demo_compatibility) + line->special = 0; + break; + + case 121: + // Blazing PlatDownWaitUpStay + if (EV_DoPlat(line,blazeDWUS,0) || demo_compatibility) + line->special = 0; + break; + + case 124: + // Secret EXIT + // killough 10/98: prevent zombies from exiting levels + // CPhipps - change for lxdoom's compatibility handling + if (bossaction || (!(thing->player && thing->player->health <= 0 && !comp[comp_zombie]))) + G_SecretExitLevel (); + break; + + case 125: + // TELEPORT MonsterONLY + if (!thing->player && + (EV_Teleport(line, side, thing) || demo_compatibility)) + line->special = 0; + break; + + case 130: + // Raise Floor Turbo + if (EV_DoFloor(line,raiseFloorTurbo) || demo_compatibility) + line->special = 0; + break; + + case 141: + // Silent Ceiling Crush & Raise + if (EV_DoCeiling(line,silentCrushAndRaise) || demo_compatibility) + line->special = 0; + break; + + // Regular walk many retriggerable + + case 72: + // Ceiling Crush + EV_DoCeiling( line, lowerAndCrush ); + break; + + case 73: + // Ceiling Crush and Raise + EV_DoCeiling(line,crushAndRaise); + break; + + case 74: + // Ceiling Crush Stop + EV_CeilingCrushStop(line); + break; + + case 75: + // Close Door + EV_DoDoor(line,closeDoor); + break; + + case 76: + // Close Door 30 + EV_DoDoor(line,close30ThenOpen); + break; + + case 77: + // Fast Ceiling Crush & Raise + EV_DoCeiling(line,fastCrushAndRaise); + break; + + case 79: + // Lights Very Dark + EV_LightTurnOn(line,35); + break; + + case 80: + // Light Turn On - brightest near + EV_LightTurnOn(line,0); + break; + + case 81: + // Light Turn On 255 + EV_LightTurnOn(line,255); + break; + + case 82: + // Lower Floor To Lowest + EV_DoFloor( line, lowerFloorToLowest ); + break; + + case 83: + // Lower Floor + EV_DoFloor(line,lowerFloor); + break; + + case 84: + // LowerAndChange + EV_DoFloor(line,lowerAndChange); + break; + + case 86: + // Open Door + EV_DoDoor(line,openDoor); + break; + + case 87: + // Perpetual Platform Raise + EV_DoPlat(line,perpetualRaise,0); + break; + + case 88: + // PlatDownWaitUp + EV_DoPlat(line,downWaitUpStay,0); + break; + + case 89: + // Platform Stop + EV_StopPlat(line); + break; + + case 90: + // Raise Door + EV_DoDoor(line,normal); + break; + + case 91: + // Raise Floor + EV_DoFloor(line,raiseFloor); + break; + + case 92: + // Raise Floor 24 + EV_DoFloor(line,raiseFloor24); + break; + + case 93: + // Raise Floor 24 And Change + EV_DoFloor(line,raiseFloor24AndChange); + break; + + case 94: + // Raise Floor Crush + EV_DoFloor(line,raiseFloorCrush); + break; + + case 95: + // Raise floor to nearest height + // and change texture. + EV_DoPlat(line,raiseToNearestAndChange,0); + break; + + case 96: + // Raise floor to shortest texture height + // on either side of lines. + EV_DoFloor(line,raiseToTexture); + break; + + case 97: + // TELEPORT! + EV_Teleport( line, side, thing ); + break; + + case 98: + // Lower Floor (TURBO) + EV_DoFloor(line,turboLower); + break; + + case 105: + // Blazing Door Raise (faster than TURBO!) + EV_DoDoor (line,blazeRaise); + break; + + case 106: + // Blazing Door Open (faster than TURBO!) + EV_DoDoor (line,blazeOpen); + break; + + case 107: + // Blazing Door Close (faster than TURBO!) + EV_DoDoor (line,blazeClose); + break; + + case 120: + // Blazing PlatDownWaitUpStay. + EV_DoPlat(line,blazeDWUS,0); + break; + + case 126: + // TELEPORT MonsterONLY. + if (!thing->player) + EV_Teleport( line, side, thing ); + break; + + case 128: + // Raise To Nearest Floor + EV_DoFloor(line,raiseFloorToNearest); + break; + + case 129: + // Raise Floor Turbo + EV_DoFloor(line,raiseFloorTurbo); + break; + + // Extended walk triggers + + // jff 1/29/98 added new linedef types to fill all functions out so that + // all have varieties SR, S1, WR, W1 + + // killough 1/31/98: "factor out" compatibility test, by + // adding inner switch qualified by compatibility flag. + // relax test to demo_compatibility + + // killough 2/16/98: Fix problems with W1 types being cleared too early + + default: + if (!demo_compatibility) + switch (line->special) + { + // Extended walk once triggers + + case 142: + // Raise Floor 512 + // 142 W1 EV_DoFloor(raiseFloor512) + if (EV_DoFloor(line,raiseFloor512)) + line->special = 0; + break; + + case 143: + // Raise Floor 24 and change + // 143 W1 EV_DoPlat(raiseAndChange,24) + if (EV_DoPlat(line,raiseAndChange,24)) + line->special = 0; + break; + + case 144: + // Raise Floor 32 and change + // 144 W1 EV_DoPlat(raiseAndChange,32) + if (EV_DoPlat(line,raiseAndChange,32)) + line->special = 0; + break; + + case 145: + // Lower Ceiling to Floor + // 145 W1 EV_DoCeiling(lowerToFloor) + if (EV_DoCeiling( line, lowerToFloor )) + line->special = 0; + break; + + case 146: + // Lower Pillar, Raise Donut + // 146 W1 EV_DoDonut() + if (EV_DoDonut(line)) + line->special = 0; + break; + + case 199: + // Lower ceiling to lowest surrounding ceiling + // 199 W1 EV_DoCeiling(lowerToLowest) + if (EV_DoCeiling(line,lowerToLowest)) + line->special = 0; + break; + + case 200: + // Lower ceiling to highest surrounding floor + // 200 W1 EV_DoCeiling(lowerToMaxFloor) + if (EV_DoCeiling(line,lowerToMaxFloor)) + line->special = 0; + break; + + case 207: + // killough 2/16/98: W1 silent teleporter (normal kind) + if (EV_SilentTeleport(line, side, thing)) + line->special = 0; + break; + + //jff 3/16/98 renumber 215->153 + case 153: //jff 3/15/98 create texture change no motion type + // Texture/Type Change Only (Trig) + // 153 W1 Change Texture/Type Only + if (EV_DoChange(line,trigChangeOnly)) + line->special = 0; + break; + + case 239: //jff 3/15/98 create texture change no motion type + // Texture/Type Change Only (Numeric) + // 239 W1 Change Texture/Type Only + if (EV_DoChange(line,numChangeOnly)) + line->special = 0; + break; + + case 219: + // Lower floor to next lower neighbor + // 219 W1 Lower Floor Next Lower Neighbor + if (EV_DoFloor(line,lowerFloorToNearest)) + line->special = 0; + break; + + case 227: + // Raise elevator next floor + // 227 W1 Raise Elevator next floor + if (EV_DoElevator(line,elevateUp)) + line->special = 0; + break; + + case 231: + // Lower elevator next floor + // 231 W1 Lower Elevator next floor + if (EV_DoElevator(line,elevateDown)) + line->special = 0; + break; + + case 235: + // Elevator to current floor + // 235 W1 Elevator to current floor + if (EV_DoElevator(line,elevateCurrent)) + line->special = 0; + break; + + case 243: //jff 3/6/98 make fit within DCK's 256 linedef types + // killough 2/16/98: W1 silent teleporter (linedef-linedef kind) + if (EV_SilentLineTeleport(line, side, thing, false)) + line->special = 0; + break; + + case 262: //jff 4/14/98 add silent line-line reversed + if (EV_SilentLineTeleport(line, side, thing, true)) + line->special = 0; + break; + + case 264: //jff 4/14/98 add monster-only silent line-line reversed + if (!thing->player && + EV_SilentLineTeleport(line, side, thing, true)) + line->special = 0; + break; + + case 266: //jff 4/14/98 add monster-only silent line-line + if (!thing->player && + EV_SilentLineTeleport(line, side, thing, false)) + line->special = 0; + break; + + case 268: //jff 4/14/98 add monster-only silent + if (!thing->player && EV_SilentTeleport(line, side, thing)) + line->special = 0; + break; + + //jff 1/29/98 end of added W1 linedef types + + // Extended walk many retriggerable + + //jff 1/29/98 added new linedef types to fill all functions + //out so that all have varieties SR, S1, WR, W1 + + case 147: + // Raise Floor 512 + // 147 WR EV_DoFloor(raiseFloor512) + EV_DoFloor(line,raiseFloor512); + break; + + case 148: + // Raise Floor 24 and Change + // 148 WR EV_DoPlat(raiseAndChange,24) + EV_DoPlat(line,raiseAndChange,24); + break; + + case 149: + // Raise Floor 32 and Change + // 149 WR EV_DoPlat(raiseAndChange,32) + EV_DoPlat(line,raiseAndChange,32); + break; + + case 150: + // Start slow silent crusher + // 150 WR EV_DoCeiling(silentCrushAndRaise) + EV_DoCeiling(line,silentCrushAndRaise); + break; + + case 151: + // RaiseCeilingLowerFloor + // 151 WR EV_DoCeiling(raiseToHighest), + // EV_DoFloor(lowerFloortoLowest) + EV_DoCeiling( line, raiseToHighest ); + EV_DoFloor( line, lowerFloorToLowest ); + break; + + case 152: + // Lower Ceiling to Floor + // 152 WR EV_DoCeiling(lowerToFloor) + EV_DoCeiling( line, lowerToFloor ); + break; + + //jff 3/16/98 renumber 153->256 + case 256: + // Build stairs, step 8 + // 256 WR EV_BuildStairs(build8) + EV_BuildStairs(line,build8); + break; + + //jff 3/16/98 renumber 154->257 + case 257: + // Build stairs, step 16 + // 257 WR EV_BuildStairs(turbo16) + EV_BuildStairs(line,turbo16); + break; + + case 155: + // Lower Pillar, Raise Donut + // 155 WR EV_DoDonut() + EV_DoDonut(line); + break; + + case 156: + // Start lights strobing + // 156 WR Lights EV_StartLightStrobing() + EV_StartLightStrobing(line); + break; + + case 157: + // Lights to dimmest near + // 157 WR Lights EV_TurnTagLightsOff() + EV_TurnTagLightsOff(line); + break; + + case 201: + // Lower ceiling to lowest surrounding ceiling + // 201 WR EV_DoCeiling(lowerToLowest) + EV_DoCeiling(line,lowerToLowest); + break; + + case 202: + // Lower ceiling to highest surrounding floor + // 202 WR EV_DoCeiling(lowerToMaxFloor) + EV_DoCeiling(line,lowerToMaxFloor); + break; + + case 208: + // killough 2/16/98: WR silent teleporter (normal kind) + EV_SilentTeleport(line, side, thing); + break; + + case 212: //jff 3/14/98 create instant toggle floor type + // Toggle floor between C and F instantly + // 212 WR Instant Toggle Floor + EV_DoPlat(line,toggleUpDn,0); + break; + + //jff 3/16/98 renumber 216->154 + case 154: //jff 3/15/98 create texture change no motion type + // Texture/Type Change Only (Trigger) + // 154 WR Change Texture/Type Only + EV_DoChange(line,trigChangeOnly); + break; + + case 240: //jff 3/15/98 create texture change no motion type + // Texture/Type Change Only (Numeric) + // 240 WR Change Texture/Type Only + EV_DoChange(line,numChangeOnly); + break; + + case 220: + // Lower floor to next lower neighbor + // 220 WR Lower Floor Next Lower Neighbor + EV_DoFloor(line,lowerFloorToNearest); + break; + + case 228: + // Raise elevator next floor + // 228 WR Raise Elevator next floor + EV_DoElevator(line,elevateUp); + break; + + case 232: + // Lower elevator next floor + // 232 WR Lower Elevator next floor + EV_DoElevator(line,elevateDown); + break; + + case 236: + // Elevator to current floor + // 236 WR Elevator to current floor + EV_DoElevator(line,elevateCurrent); + break; + + case 244: //jff 3/6/98 make fit within DCK's 256 linedef types + // killough 2/16/98: WR silent teleporter (linedef-linedef kind) + EV_SilentLineTeleport(line, side, thing, false); + break; + + case 263: //jff 4/14/98 add silent line-line reversed + EV_SilentLineTeleport(line, side, thing, true); + break; + + case 265: //jff 4/14/98 add monster-only silent line-line reversed + if (!thing->player) + EV_SilentLineTeleport(line, side, thing, true); + break; + + case 267: //jff 4/14/98 add monster-only silent line-line + if (!thing->player) + EV_SilentLineTeleport(line, side, thing, false); + break; + + case 269: //jff 4/14/98 add monster-only silent + if (!thing->player) + EV_SilentTeleport(line, side, thing); + break; + + //jff 1/29/98 end of added WR linedef types + } + break; + } +} + +// +// P_ShootSpecialLine - Gun trigger special dispatcher +// +// Called when a thing shoots a special line with bullet, shell, saw, or fist. +// +// jff 02/12/98 all G1 lines were fixed to check the result from the EV_ +// function before clearing the special. This avoids losing the function +// of the line, should the sector already be in motion when the line is +// impacted. Change is qualified by demo_compatibility. +// +void P_ShootSpecialLine +( mobj_t* thing, + line_t* line ) +{ + //jff 02/04/98 add check here for generalized linedef + if (!demo_compatibility) + { + // pointer to line function is NULL by default, set non-null if + // line special is gun triggered generalized linedef type + int (*linefunc)(line_t *line)=NULL; + + // check each range of generalized linedefs + if ((unsigned)line->special >= GenEnd) + { + // Out of range for GenFloors + } + else if ((unsigned)line->special >= GenFloorBase) + { + if (!thing->player) + if ((line->special & FloorChange) || !(line->special & FloorModel)) + return; // FloorModel is "Allow Monsters" if FloorChange is 0 + if (!comperr(comperr_zerotag) && !line->tag) //e6y //jff 2/27/98 all gun generalized types require tag + return; + + linefunc = EV_DoGenFloor; + } + else if ((unsigned)line->special >= GenCeilingBase) + { + if (!thing->player) + if ((line->special & CeilingChange) || !(line->special & CeilingModel)) + return; // CeilingModel is "Allow Monsters" if CeilingChange is 0 + if (!comperr(comperr_zerotag) && !line->tag) //e6y //jff 2/27/98 all gun generalized types require tag + return; + linefunc = EV_DoGenCeiling; + } + else if ((unsigned)line->special >= GenDoorBase) + { + if (!thing->player) + { + if (!(line->special & DoorMonster)) + return; // monsters disallowed from this door + if (line->flags & ML_SECRET) // they can't open secret doors either + return; + } + if (!comperr(comperr_zerotag) && !line->tag) //e6y //jff 3/2/98 all gun generalized types require tag + return; + linefunc = EV_DoGenDoor; + } + else if ((unsigned)line->special >= GenLockedBase) + { + if (!thing->player) + return; // monsters disallowed from unlocking doors + if (((line->special&TriggerType)==GunOnce) || ((line->special&TriggerType)==GunMany)) + { //jff 4/1/98 check for being a gun type before reporting door type + if (!P_CanUnlockGenDoor(line,thing->player)) + return; + } + else + return; + if (!comperr(comperr_zerotag) && !line->tag) //e6y //jff 2/27/98 all gun generalized types require tag + return; + + linefunc = EV_DoGenLockedDoor; + } + else if ((unsigned)line->special >= GenLiftBase) + { + if (!thing->player) + if (!(line->special & LiftMonster)) + return; // monsters disallowed + linefunc = EV_DoGenLift; + } + else if ((unsigned)line->special >= GenStairsBase) + { + if (!thing->player) + if (!(line->special & StairMonster)) + return; // monsters disallowed + if (!comperr(comperr_zerotag) && !line->tag) //e6y //jff 2/27/98 all gun generalized types require tag + return; + linefunc = EV_DoGenStairs; + } + else if ((unsigned)line->special >= GenCrusherBase) + { + if (!thing->player) + if (!(line->special & StairMonster)) + return; // monsters disallowed + if (!comperr(comperr_zerotag) && !line->tag) //e6y //jff 2/27/98 all gun generalized types require tag + return; + linefunc = EV_DoGenCrusher; + } + + if (linefunc) + switch((line->special & TriggerType) >> TriggerTypeShift) + { + case GunOnce: + if (linefunc(line)) + P_ChangeSwitchTexture(line,0); + return; + case GunMany: + if (linefunc(line)) + P_ChangeSwitchTexture(line,1); + return; + default: // if not a gun type, do nothing here + return; + } + } + + // Impacts that other things can activate. + if (!thing->player) + { + int ok = 0; + switch(line->special) + { + case 46: + // 46 GR Open door on impact weapon is monster activatable + ok = 1; + break; + } + if (!ok) + return; + } + + if (!P_CheckTag(line)) //jff 2/27/98 disallow zero tag on some types + return; + + switch(line->special) + { + case 24: + // 24 G1 raise floor to highest adjacent + if (EV_DoFloor(line,raiseFloor) || demo_compatibility) + P_ChangeSwitchTexture(line,0); + break; + + case 46: + // 46 GR open door, stay open + EV_DoDoor(line,openDoor); + P_ChangeSwitchTexture(line,1); + break; + + case 47: + // 47 G1 raise floor to nearest and change texture and type + if (EV_DoPlat(line,raiseToNearestAndChange,0) || demo_compatibility) + P_ChangeSwitchTexture(line,0); + break; + + //jff 1/30/98 added new gun linedefs here + // killough 1/31/98: added demo_compatibility check, added inner switch + + default: + if (!demo_compatibility) + switch (line->special) + { + case 197: + // Exit to next level + // killough 10/98: prevent zombies from exiting levels + if(thing->player && thing->player->health<=0 && !comp[comp_zombie]) + break; + P_ChangeSwitchTexture(line,0); + G_ExitLevel(); + break; + + case 198: + // Exit to secret level + // killough 10/98: prevent zombies from exiting levels + if(thing->player && thing->player->health<=0 && !comp[comp_zombie]) + break; + P_ChangeSwitchTexture(line,0); + G_SecretExitLevel(); + break; + //jff end addition of new gun linedefs + } + break; + } +} + + +// +// P_PlayerInSpecialSector() +// +// Called every tick frame +// that the player origin is in a special sector +// +// Changed to ignore sector types the engine does not recognize +// +void P_PlayerInSpecialSector (player_t* player) +{ + sector_t* sector; + + sector = player->mo->subsector->sector; + + // Falling, not all the way down yet? + // Sector specials don't apply in mid-air + if (player->mo->z != sector->floorheight) + return; + + // Has hit ground. + //jff add if to handle old vs generalized types + if (sector->special<32) // regular sector specials + { + switch (sector->special) + { + case 5: + // 5/10 unit damage per 31 ticks + if (!player->powers[pw_ironfeet]) + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 10); + break; + + case 7: + // 2/5 unit damage per 31 ticks + if (!player->powers[pw_ironfeet]) + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 5); + break; + + case 16: + // 10/20 unit damage per 31 ticks + case 4: + // 10/20 unit damage plus blinking light (light already spawned) + if (!player->powers[pw_ironfeet] + || (P_Random(pr_slimehurt)<5) ) // even with suit, take damage + { + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 20); + } + break; + + case 9: + // Tally player in secret sector, clear secret special + player->secretcount++; + sector->special = 0; + //e6y + if (hudadd_secretarea) + { + int sfx_id = (I_GetSfxLumpNum(&S_sfx[sfx_secret]) < 0 ? sfx_itmbk : sfx_secret); + SetCustomMessage(player - players, STSTR_SECRETFOUND, 0, 2 * TICRATE, CR_GOLD, sfx_id); + } + + break; + + case 11: + // Exit on health < 11, take 10/20 damage per 31 ticks + if (comp[comp_god]) /* killough 2/21/98: add compatibility switch */ + player->cheats &= ~CF_GODMODE; // on godmode cheat clearing + // does not affect invulnerability + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 20); + + if (player->health <= 10) + G_ExitLevel(); + break; + + default: + //jff 1/24/98 Don't exit as DOOM2 did, just ignore + break; + }; + } + else //jff 3/14/98 handle extended sector types for secrets and damage + { + switch ((sector->special&DAMAGE_MASK)>>DAMAGE_SHIFT) + { + case 0: // no damage + break; + case 1: // 2/5 damage per 31 ticks + if (!player->powers[pw_ironfeet]) + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 5); + break; + case 2: // 5/10 damage per 31 ticks + if (!player->powers[pw_ironfeet]) + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 10); + break; + case 3: // 10/20 damage per 31 ticks + if (!player->powers[pw_ironfeet] + || (P_Random(pr_slimehurt)<5)) // take damage even with suit + { + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 20); + } + break; + } + if (sector->special&SECRET_MASK) + { + player->secretcount++; + sector->special &= ~SECRET_MASK; + if (sector->special<32) // if all extended bits clear, + sector->special=0; // sector is not special anymore + //e6y + if (hudadd_secretarea) + { + int sfx_id = (I_GetSfxLumpNum(&S_sfx[sfx_secret]) < 0 ? sfx_itmbk : sfx_secret); + SetCustomMessage(player - players, STSTR_SECRETFOUND, 0, 2 * TICRATE, CR_GOLD, sfx_id); + } + } + + // phares 3/19/98: + // + // If FRICTION_MASK or PUSH_MASK is set, we don't care at this + // point, since the code to deal with those situations is + // handled by Thinkers. + + } +} + +// +// P_UpdateSpecials() +// +// Check level timer, frag counter, +// animate flats, scroll walls, +// change button textures +// +// Reads and modifies globals: +// levelTimer, levelTimeCount, +// levelFragLimit, levelFragLimitCount +// + +static dboolean levelTimer; +static int levelTimeCount; +dboolean levelFragLimit; // Ty 03/18/98 Added -frags support +int levelFragLimitCount; // Ty 03/18/98 Added -frags support + +void P_UpdateSpecials (void) +{ + anim_t* anim; + int pic; + int i; + // Downcount level timer, exit level if elapsed + if (levelTimer == true) + { + levelTimeCount--; + if (!levelTimeCount) + G_ExitLevel(); + } + + // Check frag counters, if frag limit reached, exit level // Ty 03/18/98 + // Seems like the total frags should be kept in a simple + // array somewhere, but until they are... + if (levelFragLimit == true) // we used -frags so compare count + { + int k,m,fragcount,exitflag=false; + for (k=0;k= levelFragLimitCount) exitflag = true; + if (exitflag == true) break; // skip out of the loop--we're done + } + if (exitflag == true) + G_ExitLevel(); + } + + // Animate flats and textures globally + for (anim = anims ; anim < lastanim ; anim++) + { + for (i=anim->basepic ; ibasepic+anim->numpics ; i++) + { + pic = anim->basepic + ( (leveltime/anim->speed + i)%anim->numpics ); + if (anim->istexture) + texturetranslation[i] = pic; + else + flattranslation[i] = pic; + } + } + + // Check buttons (retriggerable switches) and change texture on timeout + 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; + } + { + /* don't take the address of the switch's sound origin, + * unless in a compatibility mode. */ + mobj_t *so = (mobj_t *)buttonlist[i].soundorg; + if (default_comp[comp_sound] || compatibility_level < prboom_6_compatibility) + /* since the buttonlist array is usually zeroed out, + * button popouts generally appear to come from (0,0) */ + so = (mobj_t *)&buttonlist[i].soundorg; + S_StartSound(so, sfx_swtchn); + } + memset(&buttonlist[i],0,sizeof(button_t)); + } + } +} + +////////////////////////////////////////////////////////////////////// +// +// Sector and Line special thinker spawning at level startup +// +////////////////////////////////////////////////////////////////////// + +// +// P_SpawnSpecials +// After the map has been loaded, +// scan for specials that spawn thinkers +// + +// Parses command line parameters. +void P_SpawnSpecials (void) +{ + sector_t* sector; + int i; + + // See if -timer needs to be used. + levelTimer = false; + + i = M_CheckParm("-avg"); // Austin Virtual Gaming 20 min timer on DM play + if (i && deathmatch) + { + levelTimer = true; + levelTimeCount = 20 * 60 * TICRATE; + } + + i = M_CheckParm("-timer"); // user defined timer on game play + if (i && deathmatch) + { + int time; + time = atoi(myargv[i+1]) * 60 * TICRATE; + levelTimer = true; + levelTimeCount = time; + } + + // See if -frags has been used + levelFragLimit = false; + i = M_CheckParm("-frags"); // Ty 03/18/98 Added -frags support + if (i && deathmatch) + { + int frags; + frags = atoi(myargv[i+1]); + if (frags <= 0) frags = 10; // default 10 if no count provided + levelFragLimit = true; + levelFragLimitCount = frags; + } + + + // Init special sectors. + sector = sectors; + for (i=0 ; ispecial) + continue; + + if (sector->special&SECRET_MASK) //jff 3/15/98 count extended + totalsecret++; // secret sectors too + + switch ((demo_compatibility && !prboom_comp[PC_TRUNCATED_SECTOR_SPECIALS].state) ? + sector->special : sector->special&31) + { + case 1: + // random off + P_SpawnLightFlash (sector); + break; + + case 2: + // strobe fast + P_SpawnStrobeFlash(sector,FASTDARK,0); + break; + + case 3: + // strobe slow + P_SpawnStrobeFlash(sector,SLOWDARK,0); + break; + + case 4: + // strobe fast/death slime + P_SpawnStrobeFlash(sector,FASTDARK,0); + sector->special |= 3<special<32) //jff 3/14/98 bits don't count unless not + totalsecret++; // a generalized sector type + break; + + case 10: + // door close in 30 seconds + P_SpawnDoorCloseIn30 (sector); + break; + + case 12: + // sync strobe slow + P_SpawnStrobeFlash (sector, SLOWDARK, 1); + break; + + case 13: + // sync strobe fast + P_SpawnStrobeFlash (sector, FASTDARK, 1); + break; + + case 14: + // door raise in 5 minutes + P_SpawnDoorRaiseIn5Mins (sector, i); + break; + + case 17: + // fire flickering + P_SpawnFireFlicker(sector); + break; + } + } + + P_RemoveAllActiveCeilings(); // jff 2/22/98 use killough's scheme + + P_RemoveAllActivePlats(); // killough + + for (i = 0;i < MAXBUTTONS;i++) + memset(&buttonlist[i],0,sizeof(button_t)); + + // P_InitTagLists() must be called before P_FindSectorFromLineTag() + // or P_FindLineFromLineTag() can be called. + + P_InitTagLists(); // killough 1/30/98: Create xref tables for tags + + P_SpawnScrollers(); // killough 3/7/98: Add generalized scrollers + + // allow MBF sky transfers in all complevels + + if (comp_skytransfers || !demo_compatibility) + for (i=0; i= 0;) + sectors[s].sky = i | PL_SKYFLAT; + break; + } + + // e6y + if (demo_compatibility) + return; + + P_SpawnFriction(); // phares 3/12/98: New friction model using linedefs + + P_SpawnPushers(); // phares 3/20/98: New pusher model using linedefs + + for (i=0; iiSectorID; + for (s = -1; (s = P_FindSectorFromLineTag(lines+i,s)) >= 0;) + sectors[s].heightsec = sec; + break; + + // killough 3/16/98: Add support for setting + // floor lighting independently (e.g. lava) + case 213: + sec = sides[*lines[i].sidenum].sector->iSectorID; + for (s = -1; (s = P_FindSectorFromLineTag(lines+i,s)) >= 0;) + sectors[s].floorlightsec = sec; + break; + + // killough 4/11/98: Add support for setting + // ceiling lighting independently + case 261: + sec = sides[*lines[i].sidenum].sector->iSectorID; + for (s = -1; (s = P_FindSectorFromLineTag(lines+i,s)) >= 0;) + sectors[s].ceilinglightsec = sec; + break; + } +} + +// killough 2/28/98: +// +// This function, with the help of r_plane.c and r_bsp.c, supports generalized +// scrolling floors and walls, with optional mobj-carrying properties, e.g. +// conveyor belts, rivers, etc. A linedef with a special type affects all +// tagged sectors the same way, by creating scrolling and/or object-carrying +// properties. Multiple linedefs may be used on the same sector and are +// cumulative, although the special case of scrolling a floor and carrying +// things on it, requires only one linedef. The linedef's direction determines +// the scrolling direction, and the linedef's length determines the scrolling +// speed. This was designed so that an edge around the sector could be used to +// control the direction of the sector's scrolling, which is usually what is +// desired. +// +// Process the active scrollers. +// +// This is the main scrolling code +// killough 3/7/98 + +void T_Scroll(scroll_t *s) +{ + fixed_t dx = s->dx, dy = s->dy; + + if (s->control != -1) + { // compute scroll amounts based on a sector's height changes + fixed_t height = sectors[s->control].floorheight + + sectors[s->control].ceilingheight; + fixed_t delta = height - s->last_height; + s->last_height = height; + dx = FixedMul(dx, delta); + dy = FixedMul(dy, delta); + } + + // killough 3/14/98: Add acceleration + if (s->accel) + { + s->vdx = dx += s->vdx; + s->vdy = dy += s->vdy; + } + + if (!(dx | dy)) // no-op if both (x,y) offsets 0 + return; + + switch (s->type) + { + side_t *side; + sector_t *sec; + fixed_t height, waterheight; // killough 4/4/98: add waterheight + msecnode_t *node; + mobj_t *thing; + + case sc_side: // killough 3/7/98: Scroll wall texture + side = sides + s->affectee; + side->textureoffset += dx; + side->rowoffset += dy; + break; + + case sc_floor: // killough 3/7/98: Scroll floor texture + sec = sectors + s->affectee; + sec->floor_xoffs += dx; + sec->floor_yoffs += dy; + break; + + case sc_ceiling: // killough 3/7/98: Scroll ceiling texture + sec = sectors + s->affectee; + sec->ceiling_xoffs += dx; + sec->ceiling_yoffs += dy; + break; + + case sc_carry: + + // killough 3/7/98: Carry things on floor + // killough 3/20/98: use new sector list which reflects true members + // killough 3/27/98: fix carrier bug + // killough 4/4/98: Underwater, carry things even w/o gravity + + sec = sectors + s->affectee; + height = sec->floorheight; + waterheight = sec->heightsec != -1 && + sectors[sec->heightsec].floorheight > height ? + sectors[sec->heightsec].floorheight : INT_MIN; + + for (node = sec->touching_thinglist; node; node = node->m_snext) + if (!((thing = node->m_thing)->flags & MF_NOCLIP) && + (!(thing->flags & MF_NOGRAVITY || thing->z > height) || + thing->z < waterheight)) + { + // Move objects only if on floor or underwater, + // non-floating, and clipped. + thing->momx += dx; + thing->momy += dy; + } + break; + + case sc_carry_ceiling: // to be added later + break; + } +} + +// +// Add_Scroller() +// +// Add a generalized scroller to the thinker list. +// +// type: the enumerated type of scrolling: floor, ceiling, floor carrier, +// wall, floor carrier & scroller +// +// (dx,dy): the direction and speed of the scrolling or its acceleration +// +// control: the sector whose heights control this scroller's effect +// remotely, or -1 if no control sector +// +// affectee: the index of the affected object (sector or sidedef) +// +// accel: non-zero if this is an accelerative effect +// + +static void Add_Scroller(int type, fixed_t dx, fixed_t dy, + int control, int affectee, int accel) +{ + scroll_t *s = Z_Malloc(sizeof *s, PU_LEVSPEC, 0); + s->thinker.function = T_Scroll; + s->type = type; + s->dx = dx; + s->dy = dy; + s->accel = accel; + s->vdx = s->vdy = 0; + if ((s->control = control) != -1) + s->last_height = + sectors[control].floorheight + sectors[control].ceilingheight; + s->affectee = affectee; + P_AddThinker(&s->thinker); +} + +// Adds wall scroller. Scroll amount is rotated with respect to wall's +// linedef first, so that scrolling towards the wall in a perpendicular +// direction is translated into vertical motion, while scrolling along +// the wall in a parallel direction is translated into horizontal motion. +// +// killough 5/25/98: cleaned up arithmetic to avoid drift due to roundoff +// +// killough 10/98: +// fix scrolling aliasing problems, caused by long linedefs causing overflowing + +static void Add_WallScroller(fixed_t dx, fixed_t dy, const line_t *l, + int control, int accel) +{ + fixed_t x = D_abs(l->dx), y = D_abs(l->dy), d; + if (y > x) + d = x, x = y, y = d; + d = FixedDiv(x, finesine[(tantoangle[FixedDiv(y,x) >> DBITS] + ANG90) + >> ANGLETOFINESHIFT]); + + // CPhipps - Import scroller calc overflow fix, compatibility optioned + if (compatibility_level >= lxdoom_1_compatibility) { + x = (fixed_t)(((int_64_t)dy * -(int_64_t)l->dy - (int_64_t)dx * (int_64_t)l->dx) / (int_64_t)d); // killough 10/98: + y = (fixed_t)(((int_64_t)dy * (int_64_t)l->dx - (int_64_t)dx * (int_64_t)l->dy) / (int_64_t)d); // Use long long arithmetic + } else { + x = -FixedDiv(FixedMul(dy, l->dy) + FixedMul(dx, l->dx), d); + y = -FixedDiv(FixedMul(dx, l->dy) - FixedMul(dy, l->dx), d); + } + Add_Scroller(sc_side, x, y, control, *l->sidenum, accel); +} + +// Amount (dx,dy) vector linedef is shifted right to get scroll amount +#define SCROLL_SHIFT 5 + +// Factor to scale scrolling effect into mobj-carrying properties = 3/32. +// (This is so scrolling floors and objects on them can move at same speed.) +#define CARRYFACTOR ((fixed_t)(FRACUNIT*.09375)) + +// Initialize the scrollers +static void P_SpawnScrollers(void) +{ + int i; + line_t *l = lines; + + for (i=0;idx >> SCROLL_SHIFT; // direction and speed of scrolling + fixed_t dy = l->dy >> SCROLL_SHIFT; + int control = -1, accel = 0; // no control sector or acceleration + int special = l->special; + if (demo_compatibility && special!=48) continue;//e6y + + // killough 3/7/98: Types 245-249 are same as 250-254 except that the + // first side's sector's heights cause scrolling when they change, and + // this linedef controls the direction and speed of the scrolling. The + // most complicated linedef since donuts, but powerful :) + // + // killough 3/15/98: Add acceleration. Types 214-218 are the same but + // are accelerative. + + if (special >= 245 && special <= 249) // displacement scrollers + { + special += 250-245; + control = sides[*l->sidenum].sector->iSectorID; + } + else + if (special >= 214 && special <= 218) // accelerative scrollers + { + accel = 1; + special += 250-214; + control = sides[*l->sidenum].sector->iSectorID; + } + + switch (special) + { + register int s; + + case 250: // scroll effect ceiling + for (s=-1; (s = P_FindSectorFromLineTag(l,s)) >= 0;) + Add_Scroller(sc_ceiling, -dx, dy, control, s, accel); + break; + + case 251: // scroll effect floor + case 253: // scroll and carry objects on floor + for (s=-1; (s = P_FindSectorFromLineTag(l,s)) >= 0;) + Add_Scroller(sc_floor, -dx, dy, control, s, accel); + if (special != 253) + break; + // fallthrough + + case 252: // carry objects on floor + dx = FixedMul(dx,CARRYFACTOR); + dy = FixedMul(dy,CARRYFACTOR); + for (s=-1; (s = P_FindSectorFromLineTag(l,s)) >= 0;) + Add_Scroller(sc_carry, dx, dy, control, s, accel); + break; + + // killough 3/1/98: scroll wall according to linedef + // (same direction and speed as scrolling floors) + case 254: + if (l->tag == 0 && comperr(comperr_zerotag)) + { + Add_WallScroller(dx, dy, l, control, accel); + } + else + { + for (s=-1; (s = P_FindLineFromLineTag(l,s)) >= 0;) + if (s != i) + Add_WallScroller(dx, dy, lines+s, control, accel); + } + break; + + case 255: // killough 3/2/98: scroll according to sidedef offsets + s = lines[i].sidenum[0]; + Add_Scroller(sc_side, -sides[s].textureoffset, + sides[s].rowoffset, -1, s, accel); + break; + + case 48: // scroll first side + Add_Scroller(sc_side, FRACUNIT, 0, -1, lines[i].sidenum[0], accel); + break; + + case 85: // jff 1/30/98 2-way scroll + Add_Scroller(sc_side, -FRACUNIT, 0, -1, lines[i].sidenum[0], accel); + break; + } + } +} + +// e6y +// restored boom's friction code + +///////////////////////////// +// +// Add a friction thinker to the thinker list +// +// Add_Friction adds a new friction thinker to the list of active thinkers. +// + +static void Add_Friction(int friction, int movefactor, int affectee) +{ + friction_t *f = Z_Malloc(sizeof *f, PU_LEVSPEC, 0); + + f->thinker.function/*.acp1*/ = /*(actionf_p1) */T_Friction; + f->friction = friction; + f->movefactor = movefactor; + f->affectee = affectee; + P_AddThinker(&f->thinker); +} + +///////////////////////////// +// +// This is where abnormal friction is applied to objects in the sectors. +// A friction thinker has been spawned for each sector where less or +// more friction should be applied. The amount applied is proportional to +// the length of the controlling linedef. + +void T_Friction(friction_t *f) +{ + sector_t *sec; + mobj_t *thing; + msecnode_t* node; + + if (compatibility || !variable_friction) + return; + + sec = sectors + f->affectee; + + // Be sure the special sector type is still turned on. If so, proceed. + // Else, bail out; the sector type has been changed on us. + + if (!(sec->special & FRICTION_MASK)) + return; + + // Assign the friction value to players on the floor, non-floating, + // and clipped. Normally the object's friction value is kept at + // ORIG_FRICTION and this thinker changes it for icy or muddy floors. + + // In Phase II, you can apply friction to Things other than players. + + // When the object is straddling sectors with the same + // floorheight that have different frictions, use the lowest + // friction value (muddy has precedence over icy). + + node = sec->touching_thinglist; // things touching this sector + while (node) + { + thing = node->m_thing; + if (thing->player && + !(thing->flags & (MF_NOGRAVITY | MF_NOCLIP)) && + thing->z <= sec->floorheight) + { + if ((thing->friction == ORIG_FRICTION) || // normal friction? + (f->friction < thing->friction)) + { + thing->friction = f->friction; + thing->movefactor = f->movefactor; + } + } + node = node->m_snext; + } +} + + +// killough 3/7/98 -- end generalized scroll effects + +//////////////////////////////////////////////////////////////////////////// +// +// FRICTION EFFECTS +// +// phares 3/12/98: Start of friction effects +// +// As the player moves, friction is applied by decreasing the x and y +// momentum values on each tic. By varying the percentage of decrease, +// we can simulate muddy or icy conditions. In mud, the player slows +// down faster. In ice, the player slows down more slowly. +// +// The amount of friction change is controlled by the length of a linedef +// with type 223. A length < 100 gives you mud. A length > 100 gives you ice. +// +// Also, each sector where these effects are to take place is given a +// new special type _______. Changing the type value at runtime allows +// these effects to be turned on or off. +// +// Sector boundaries present problems. The player should experience these +// friction changes only when his feet are touching the sector floor. At +// sector boundaries where floor height changes, the player can find +// himself still 'in' one sector, but with his feet at the floor level +// of the next sector (steps up or down). To handle this, Thinkers are used +// in icy/muddy sectors. These thinkers examine each object that is touching +// their sectors, looking for players whose feet are at the same level as +// their floors. Players satisfying this condition are given new friction +// values that are applied by the player movement code later. +// +// killough 8/28/98: +// +// Completely redid code, which did not need thinkers, and which put a heavy +// drag on CPU. Friction is now a property of sectors, NOT objects inside +// them. All objects, not just players, are affected by it, if they touch +// the sector's floor. Code simpler and faster, only calling on friction +// calculations when an object needs friction considered, instead of doing +// friction calculations on every sector during every tic. +// +// Although this -might- ruin Boom demo sync involving friction, it's the only +// way, short of code explosion, to fix the original design bug. Fixing the +// design bug in Boom's original friction code, while maintaining demo sync +// under every conceivable circumstance, would double or triple code size, and +// would require maintenance of buggy legacy code which is only useful for old +// demos. Doom demos, which are more important IMO, are not affected by this +// change. +// +///////////////////////////// +// +// Initialize the sectors where friction is increased or decreased + +static void P_SpawnFriction(void) +{ + int i; + line_t *l = lines; + + // killough 8/28/98: initialize all sectors to normal friction first + for (i = 0; i < numsectors; i++) + { + sectors[i].friction = ORIG_FRICTION; + sectors[i].movefactor = ORIG_FRICTION_FACTOR; + } + + for (i = 0 ; i < numlines ; i++,l++) + if (l->special == 223) + { + int length = P_AproxDistance(l->dx,l->dy)>>FRACBITS; + int friction = (0x1EB8*length)/0x80 + 0xD000; + int movefactor, s; + + // The following check might seem odd. At the time of movement, + // the move distance is multiplied by 'friction/0x10000', so a + // higher friction value actually means 'less friction'. + + if (friction > ORIG_FRICTION) // ice + movefactor = ((0x10092 - friction)*(0x70))/0x158; + else + movefactor = ((friction - 0xDB34)*(0xA))/0x80; + + if (mbf_features) + { // killough 8/28/98: prevent odd situations + if (friction > FRACUNIT) + friction = FRACUNIT; + if (friction < 0) + friction = 0; + if (movefactor < 32) + movefactor = 32; + } + + for (s = -1; (s = P_FindSectorFromLineTag(l,s)) >= 0 ; ) + { + // killough 8/28/98: + // + // Instead of spawning thinkers, which are slow and expensive, + // modify the sector's own friction values. Friction should be + // a property of sectors, not objects which reside inside them. + // Original code scanned every object in every friction sector + // on every tic, adjusting its friction, putting unnecessary + // drag on CPU. New code adjusts friction of sector only once + // at level startup, and then uses this friction value. + + //e6y: boom's friction code for boom compatibility + if (!demo_compatibility && !mbf_features && !prboom_comp[PC_PRBOOM_FRICTION].state) + Add_Friction(friction,movefactor,s); + + sectors[s].friction = friction; + sectors[s].movefactor = movefactor; + } + } +} + +// +// phares 3/12/98: End of friction effects +// +//////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////// +// +// PUSH/PULL EFFECT +// +// phares 3/20/98: Start of push/pull effects +// +// This is where push/pull effects are applied to objects in the sectors. +// +// There are four kinds of push effects +// +// 1) Pushing Away +// +// Pushes you away from a point source defined by the location of an +// MT_PUSH Thing. The force decreases linearly with distance from the +// source. This force crosses sector boundaries and is felt w/in a circle +// whose center is at the MT_PUSH. The force is felt only if the point +// MT_PUSH can see the target object. +// +// 2) Pulling toward +// +// Same as Pushing Away except you're pulled toward an MT_PULL point +// source. This force crosses sector boundaries and is felt w/in a circle +// whose center is at the MT_PULL. The force is felt only if the point +// MT_PULL can see the target object. +// +// 3) Wind +// +// Pushes you in a constant direction. Full force above ground, half +// force on the ground, nothing if you're below it (water). +// +// 4) Current +// +// Pushes you in a constant direction. No force above ground, full +// force if on the ground or below it (water). +// +// The magnitude of the force is controlled by the length of a controlling +// linedef. The force vector for types 3 & 4 is determined by the angle +// of the linedef, and is constant. +// +// For each sector where these effects occur, the sector special type has +// to have the PUSH_MASK bit set. If this bit is turned off by a switch +// at run-time, the effect will not occur. The controlling sector for +// types 1 & 2 is the sector containing the MT_PUSH/MT_PULL Thing. + + +#define PUSH_FACTOR 7 + +///////////////////////////// +// +// Add a push thinker to the thinker list + +static void Add_Pusher(int type, int x_mag, int y_mag, mobj_t* source, int affectee) +{ + pusher_t *p = Z_Malloc(sizeof *p, PU_LEVSPEC, 0); + + p->thinker.function = T_Pusher; + p->source = source; + p->type = type; + p->x_mag = x_mag>>FRACBITS; + p->y_mag = y_mag>>FRACBITS; + p->magnitude = P_AproxDistance(p->x_mag,p->y_mag); + if (source) // point source exist? + { + p->radius = (p->magnitude)<<(FRACBITS+1); // where force goes to zero + p->x = p->source->x; + p->y = p->source->y; + } + p->affectee = affectee; + P_AddThinker(&p->thinker); +} + +///////////////////////////// +// +// PIT_PushThing determines the angle and magnitude of the effect. +// The object's x and y momentum values are changed. +// +// tmpusher belongs to the point source (MT_PUSH/MT_PULL). +// +// killough 10/98: allow to affect things besides players + +pusher_t* tmpusher; // pusher structure for blockmap searches + +static dboolean PIT_PushThing(mobj_t* thing) +{ + /* killough 10/98: made more general */ + if (!mbf_features ? + thing->player && !(thing->flags & (MF_NOCLIP | MF_NOGRAVITY)) : + (sentient(thing) || thing->flags & MF_SHOOTABLE) && + !(thing->flags & MF_NOCLIP)) + { + angle_t pushangle; + fixed_t speed; + fixed_t sx = tmpusher->x; + fixed_t sy = tmpusher->y; + + speed = (tmpusher->magnitude - + ((P_AproxDistance(thing->x - sx,thing->y - sy) + >>FRACBITS)>>1))<<(FRACBITS-PUSH_FACTOR-1); + + // killough 10/98: make magnitude decrease with square + // of distance, making it more in line with real nature, + // so long as it's still in range with original formula. + // + // Removes angular distortion, and makes effort required + // to stay close to source, grow increasingly hard as you + // get closer, as expected. Still, it doesn't consider z :( + + if (speed > 0 && mbf_features) + { + int x = (thing->x-sx) >> FRACBITS; + int y = (thing->y-sy) >> FRACBITS; + speed = (int)(((uint_64_t) tmpusher->magnitude << 23) / (x*x+y*y+1)); + } + + // If speed <= 0, you're outside the effective radius. You also have + // to be able to see the push/pull source point. + + if (speed > 0 && P_CheckSight(thing,tmpusher->source)) + { + pushangle = R_PointToAngle2(thing->x,thing->y,sx,sy); + if (tmpusher->source->type == MT_PUSH) + pushangle += ANG180; // away + pushangle >>= ANGLETOFINESHIFT; + thing->momx += FixedMul(speed,finecosine[pushangle]); + thing->momy += FixedMul(speed,finesine[pushangle]); + } + } + return true; +} + +///////////////////////////// +// +// T_Pusher looks for all objects that are inside the radius of +// the effect. +// + +void T_Pusher(pusher_t *p) +{ + sector_t *sec; + mobj_t *thing; + msecnode_t* node; + int xspeed,yspeed; + int xl,xh,yl,yh,bx,by; + int radius; + int ht = 0; + + if (!allow_pushers) + return; + + sec = sectors + p->affectee; + + // Be sure the special sector type is still turned on. If so, proceed. + // Else, bail out; the sector type has been changed on us. + + if (!(sec->special & PUSH_MASK)) + return; + + // For constant pushers (wind/current) there are 3 situations: + // + // 1) Affected Thing is above the floor. + // + // Apply the full force if wind, no force if current. + // + // 2) Affected Thing is on the ground. + // + // Apply half force if wind, full force if current. + // + // 3) Affected Thing is below the ground (underwater effect). + // + // Apply no force if wind, full force if current. + + if (p->type == p_push) + { + + // Seek out all pushable things within the force radius of this + // point pusher. Crosses sectors, so use blockmap. + + tmpusher = p; // MT_PUSH/MT_PULL point source + radius = p->radius; // where force goes to zero + tmbbox[BOXTOP] = p->y + radius; + tmbbox[BOXBOTTOM] = p->y - radius; + tmbbox[BOXRIGHT] = p->x + radius; + tmbbox[BOXLEFT] = p->x - radius; + + xl = P_GetSafeBlockX(tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS); + xh = P_GetSafeBlockX(tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS); + yl = P_GetSafeBlockY(tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS); + yh = P_GetSafeBlockY(tmbbox[BOXTOP] - bmaporgy + MAXRADIUS); + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + P_BlockThingsIterator(bx,by,PIT_PushThing); + return; + } + + // constant pushers p_wind and p_current + + if (sec->heightsec != -1) // special water sector? + ht = sectors[sec->heightsec].floorheight; + node = sec->touching_thinglist; // things touching this sector + for ( ; node ; node = node->m_snext) + { + thing = node->m_thing; + if (!thing->player || (thing->flags & (MF_NOGRAVITY | MF_NOCLIP))) + continue; + if (p->type == p_wind) + { + if (sec->heightsec == -1) // NOT special water sector + if (thing->z > thing->floorz) // above ground + { + xspeed = p->x_mag; // full force + yspeed = p->y_mag; + } + else // on ground + { + xspeed = (p->x_mag)>>1; // half force + yspeed = (p->y_mag)>>1; + } + else // special water sector + { + if (thing->z > ht) // above ground + { + xspeed = p->x_mag; // full force + yspeed = p->y_mag; + } + else if (thing->player->viewz < ht) // underwater + xspeed = yspeed = 0; // no force + else // wading in water + { + xspeed = (p->x_mag)>>1; // half force + yspeed = (p->y_mag)>>1; + } + } + } + else // p_current + { + if (sec->heightsec == -1) // NOT special water sector + if (thing->z > sec->floorheight) // above ground + xspeed = yspeed = 0; // no force + else // on ground + { + xspeed = p->x_mag; // full force + yspeed = p->y_mag; + } + else // special water sector + if (thing->z > ht) // above ground + xspeed = yspeed = 0; // no force + else // underwater + { + xspeed = p->x_mag; // full force + yspeed = p->y_mag; + } + } + thing->momx += xspeed<<(FRACBITS-PUSH_FACTOR); + thing->momy += yspeed<<(FRACBITS-PUSH_FACTOR); + } +} + +///////////////////////////// +// +// P_GetPushThing() returns a pointer to an MT_PUSH or MT_PULL thing, +// NULL otherwise. + +mobj_t* P_GetPushThing(int s) +{ + mobj_t* thing; + sector_t* sec; + + sec = sectors + s; + thing = sec->thinglist; + while (thing) + { + switch(thing->type) + { + case MT_PUSH: + case MT_PULL: + return thing; + default: + break; + } + thing = thing->snext; + } + return NULL; +} + +///////////////////////////// +// +// Initialize the sectors where pushers are present +// + +static void P_SpawnPushers(void) +{ + int i; + line_t *l = lines; + register int s; + mobj_t* thing; + + for (i = 0 ; i < numlines ; i++,l++) + switch(l->special) + { + case 224: // wind + for (s = -1; (s = P_FindSectorFromLineTag(l,s)) >= 0 ; ) + Add_Pusher(p_wind,l->dx,l->dy,NULL,s); + break; + case 225: // current + for (s = -1; (s = P_FindSectorFromLineTag(l,s)) >= 0 ; ) + Add_Pusher(p_current,l->dx,l->dy,NULL,s); + break; + case 226: // push/pull + for (s = -1; (s = P_FindSectorFromLineTag(l,s)) >= 0 ; ) + { + thing = P_GetPushThing(s); + if (thing) // No MT_P* means no effect + Add_Pusher(p_push,l->dx,l->dy,thing,s); + } + break; + } +} + +// +// phares 3/20/98: End of Pusher effects +// +//////////////////////////////////////////////////////////////////////////// diff --git a/src/p_spec.h b/src/p_spec.h new file mode 100644 index 0000000..ec597c1 --- /dev/null +++ b/src/p_spec.h @@ -0,0 +1,1165 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: definitions, declarations and prototypes for specials + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_SPEC__ +#define __P_SPEC__ + +#include "r_defs.h" +#include "d_player.h" + +// Define values for map objects +#define MO_TELEPORTMAN 14 + +// p_floor + +#define ELEVATORSPEED (FRACUNIT*4) +#define FLOORSPEED FRACUNIT + +// p_ceilng + +#define CEILSPEED FRACUNIT +#define CEILWAIT 150 + +// p_doors + +#define VDOORSPEED (FRACUNIT*2) +#define VDOORWAIT 150 + +// p_plats + +#define PLATWAIT 3 +#define PLATSPEED FRACUNIT + +// p_switch + +// 4 players, 4 buttons each at once, max. +// killough 2/14/98: redefine in terms of MAXPLAYERS +#define MAXBUTTONS (MAXPLAYERS*4) + +// 1 second, in ticks. +#define BUTTONTIME TICRATE + +// p_lights + +#define GLOWSPEED 8 +#define STROBEBRIGHT 5 +#define FASTDARK 15 +#define SLOWDARK 35 + +//jff 3/14/98 add bits and shifts for generalized sector types + +#define DAMAGE_MASK 0x60 +#define DAMAGE_SHIFT 5 +#define SECRET_MASK 0x80 +#define SECRET_SHIFT 7 +#define FRICTION_MASK 0x100 +#define FRICTION_SHIFT 8 +#define PUSH_MASK 0x200 +#define PUSH_SHIFT 9 + +//jff 02/04/98 Define masks, shifts, for fields in +// generalized linedef types + +#define GenEnd 0x8000 +#define GenFloorBase 0x6000 +#define GenCeilingBase 0x4000 +#define GenDoorBase 0x3c00 +#define GenLockedBase 0x3800 +#define GenLiftBase 0x3400 +#define GenStairsBase 0x3000 +#define GenCrusherBase 0x2F80 + +#define TriggerType 0x0007 +#define TriggerTypeShift 0 + +// define masks and shifts for the floor type fields + +#define FloorCrush 0x1000 +#define FloorChange 0x0c00 +#define FloorTarget 0x0380 +#define FloorDirection 0x0040 +#define FloorModel 0x0020 +#define FloorSpeed 0x0018 + +#define FloorCrushShift 12 +#define FloorChangeShift 10 +#define FloorTargetShift 7 +#define FloorDirectionShift 6 +#define FloorModelShift 5 +#define FloorSpeedShift 3 + +// define masks and shifts for the ceiling type fields + +#define CeilingCrush 0x1000 +#define CeilingChange 0x0c00 +#define CeilingTarget 0x0380 +#define CeilingDirection 0x0040 +#define CeilingModel 0x0020 +#define CeilingSpeed 0x0018 + +#define CeilingCrushShift 12 +#define CeilingChangeShift 10 +#define CeilingTargetShift 7 +#define CeilingDirectionShift 6 +#define CeilingModelShift 5 +#define CeilingSpeedShift 3 + +// define masks and shifts for the lift type fields + +#define LiftTarget 0x0300 +#define LiftDelay 0x00c0 +#define LiftMonster 0x0020 +#define LiftSpeed 0x0018 + +#define LiftTargetShift 8 +#define LiftDelayShift 6 +#define LiftMonsterShift 5 +#define LiftSpeedShift 3 + +// define masks and shifts for the stairs type fields + +#define StairIgnore 0x0200 +#define StairDirection 0x0100 +#define StairStep 0x00c0 +#define StairMonster 0x0020 +#define StairSpeed 0x0018 + +#define StairIgnoreShift 9 +#define StairDirectionShift 8 +#define StairStepShift 6 +#define StairMonsterShift 5 +#define StairSpeedShift 3 + +// define masks and shifts for the crusher type fields + +#define CrusherSilent 0x0040 +#define CrusherMonster 0x0020 +#define CrusherSpeed 0x0018 + +#define CrusherSilentShift 6 +#define CrusherMonsterShift 5 +#define CrusherSpeedShift 3 + +// define masks and shifts for the door type fields + +#define DoorDelay 0x0300 +#define DoorMonster 0x0080 +#define DoorKind 0x0060 +#define DoorSpeed 0x0018 + +#define DoorDelayShift 8 +#define DoorMonsterShift 7 +#define DoorKindShift 5 +#define DoorSpeedShift 3 + +// define masks and shifts for the locked door type fields + +#define LockedNKeys 0x0200 +#define LockedKey 0x01c0 +#define LockedKind 0x0020 +#define LockedSpeed 0x0018 + +#define LockedNKeysShift 9 +#define LockedKeyShift 6 +#define LockedKindShift 5 +#define LockedSpeedShift 3 + +// +// Animating textures and planes +// There is another anim_t used in wi_stuff, unrelated. +// +typedef struct +{ + dboolean istexture; + int picnum; + int basepic; + int numpics; + int speed; + +} anim_t; + +//e6y +typedef struct +{ + int index; + anim_t *anim; +} TAnimItemParam; +extern TAnimItemParam *anim_flats; +extern TAnimItemParam *anim_textures; + +// define names for the TriggerType field of the general linedefs + +typedef enum +{ + WalkOnce, + WalkMany, + SwitchOnce, + SwitchMany, + GunOnce, + GunMany, + PushOnce, + PushMany, +} triggertype_e; + +// define names for the Speed field of the general linedefs + +typedef enum +{ + SpeedSlow, + SpeedNormal, + SpeedFast, + SpeedTurbo, +} motionspeed_e; + +// define names for the Target field of the general floor + +typedef enum +{ + FtoHnF, + FtoLnF, + FtoNnF, + FtoLnC, + FtoC, + FbyST, + Fby24, + Fby32, +} floortarget_e; + +// define names for the Changer Type field of the general floor + +typedef enum +{ + FNoChg, + FChgZero, + FChgTxt, + FChgTyp, +} floorchange_e; + +// define names for the Change Model field of the general floor + +typedef enum +{ + FTriggerModel, + FNumericModel, +} floormodel_t; + +// define names for the Target field of the general ceiling + +typedef enum +{ + CtoHnC, + CtoLnC, + CtoNnC, + CtoHnF, + CtoF, + CbyST, + Cby24, + Cby32, +} ceilingtarget_e; + +// define names for the Changer Type field of the general ceiling + +typedef enum +{ + CNoChg, + CChgZero, + CChgTxt, + CChgTyp, +} ceilingchange_e; + +// define names for the Change Model field of the general ceiling + +typedef enum +{ + CTriggerModel, + CNumericModel, +} ceilingmodel_t; + +// define names for the Target field of the general lift + +typedef enum +{ + F2LnF, + F2NnF, + F2LnC, + LnF2HnF, +} lifttarget_e; + +// define names for the door Kind field of the general ceiling + +typedef enum +{ + OdCDoor, + ODoor, + CdODoor, + CDoor, +} doorkind_e; + +// define names for the locked door Kind field of the general ceiling + +typedef enum +{ + AnyKey, + RCard, + BCard, + YCard, + RSkull, + BSkull, + YSkull, + AllKeys, +} keykind_e; + +////////////////////////////////////////////////////////////////// +// +// enums for classes of linedef triggers +// +////////////////////////////////////////////////////////////////// + +//jff 2/23/98 identify the special classes that can share sectors + +typedef enum +{ + floor_special, + ceiling_special, + lighting_special, +} special_e; + +//jff 3/15/98 pure texture/type change for better generalized support +typedef enum +{ + trigChangeOnly, + numChangeOnly, +} change_e; + +// p_plats + +typedef enum +{ + up, + down, + waiting, + in_stasis +} plat_e; + +typedef enum +{ + perpetualRaise, + downWaitUpStay, + raiseAndChange, + raiseToNearestAndChange, + blazeDWUS, + genLift, //jff added to support generalized Plat types + genPerpetual, + toggleUpDn, //jff 3/14/98 added to support instant toggle type + +} plattype_e; + +// p_doors + +typedef enum +{ + normal, + close30ThenOpen, + closeDoor, + openDoor, + raiseIn5Mins, + blazeRaise, + blazeOpen, + blazeClose, + + //jff 02/05/98 add generalize door types + genRaise, + genBlazeRaise, + genOpen, + genBlazeOpen, + genClose, + genBlazeClose, + genCdO, + genBlazeCdO, +} vldoor_e; + +// p_ceilng + +typedef enum +{ + lowerToFloor, + raiseToHighest, + lowerToLowest, + lowerToMaxFloor, + lowerAndCrush, + crushAndRaise, + fastCrushAndRaise, + silentCrushAndRaise, + + //jff 02/04/98 add types for generalized ceiling mover + genCeiling, + genCeilingChg, + genCeilingChg0, + genCeilingChgT, + + //jff 02/05/98 add types for generalized ceiling mover + genCrusher, + genSilentCrusher, + +} ceiling_e; + +// p_floor + +typedef enum +{ + // lower floor to highest surrounding floor + lowerFloor, + + // lower floor to lowest surrounding floor + lowerFloorToLowest, + + // lower floor to highest surrounding floor VERY FAST + turboLower, + + // raise floor to lowest surrounding CEILING + raiseFloor, + + // raise floor to next highest surrounding floor + raiseFloorToNearest, + + //jff 02/03/98 lower floor to next lowest neighbor + lowerFloorToNearest, + + //jff 02/03/98 lower floor 24 absolute + lowerFloor24, + + //jff 02/03/98 lower floor 32 absolute + lowerFloor32Turbo, + + // raise floor to shortest height texture around it + raiseToTexture, + + // lower floor to lowest surrounding floor + // and change floorpic + lowerAndChange, + + raiseFloor24, + + //jff 02/03/98 raise floor 32 absolute + raiseFloor32Turbo, + + raiseFloor24AndChange, + raiseFloorCrush, + + // raise to next highest floor, turbo-speed + raiseFloorTurbo, + donutRaise, + raiseFloor512, + + //jff 02/04/98 add types for generalized floor mover + genFloor, + genFloorChg, + genFloorChg0, + genFloorChgT, + + //new types for stair builders + buildStair, + genBuildStair, +} floor_e; + +typedef enum +{ + build8, // slowly build by 8 + turbo16 // quickly build by 16 + +} stair_e; + +typedef enum +{ + elevateUp, + elevateDown, + elevateCurrent, +} elevator_e; + +////////////////////////////////////////////////////////////////// +// +// general enums +// +////////////////////////////////////////////////////////////////// + +// texture type enum +typedef enum +{ + top, + middle, + bottom + +} bwhere_e; + +// crush check returns +typedef enum +{ + ok, + crushed, + pastdest +} result_e; + +////////////////////////////////////////////////////////////////// +// +// linedef and sector special data types +// +////////////////////////////////////////////////////////////////// + +// p_switch + +// switch animation structure type + +#if defined(__MWERKS__) +#pragma options align=packed +#endif + +typedef struct +{ + char name1[9]; + char name2[9]; + short episode; +} PACKEDATTR switchlist_t; //jff 3/23/98 pack to read from memory + +#if defined(__MWERKS__) +#pragma options align=reset +#endif + +typedef struct +{ + line_t* line; + bwhere_e where; + int btexture; + int btimer; + mobj_t* soundorg; + +} button_t; + +// 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_plats + +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; + dboolean crush; + int tag; + plattype_e type; + + struct platlist *list; // killough +} plat_t; + +// New limit-free plat structure -- killough + +typedef struct platlist { + plat_t *plat; + struct platlist *next,**prev; +} platlist_t; + +// p_ceilng + +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; + + //jff 1/31/98 keep track of line door is triggered by + line_t *line; + + /* killough 10/98: sector tag for gradual lighting effects */ + int lighttag; +} vldoor_t; + +// p_doors + +typedef struct +{ + thinker_t thinker; + ceiling_e type; + sector_t* sector; + fixed_t bottomheight; + fixed_t topheight; + fixed_t speed; + fixed_t oldspeed; + dboolean crush; + + //jff 02/04/98 add these to support ceiling changers + int newspecial; + int oldspecial; //jff 3/14/98 add to fix bug in change transfers + short texture; + + // 1 = up, 0 = waiting, -1 = down + int direction; + + // ID + int tag; + int olddirection; + struct ceilinglist *list; // jff 2/22/98 copied from killough's plats +} ceiling_t; + +typedef struct ceilinglist { + ceiling_t *ceiling; + struct ceilinglist *next,**prev; +} ceilinglist_t; + +// p_floor + +typedef struct +{ + thinker_t thinker; + floor_e type; + dboolean crush; + sector_t* sector; + int direction; + int newspecial; + int oldspecial; //jff 3/14/98 add to fix bug in change transfers + short texture; + fixed_t floordestheight; + fixed_t speed; + +} floormove_t; + +typedef struct +{ + thinker_t thinker; + elevator_e type; + sector_t* sector; + int direction; + fixed_t floordestheight; + fixed_t ceilingdestheight; + fixed_t speed; +} elevator_t; + +// p_spec + +// killough 3/7/98: Add generalized scroll effects + +typedef struct { + thinker_t thinker; // Thinker structure for scrolling + fixed_t dx, dy; // (dx,dy) scroll speeds + int affectee; // Number of affected sidedef, sector, tag, or whatever + int control; // Control sector (-1 if none) used to control scrolling + fixed_t last_height; // Last known height of control sector + fixed_t vdx, vdy; // Accumulated velocity if accelerative + int accel; // Whether it's accelerative + enum + { + sc_side, + sc_floor, + sc_ceiling, + sc_carry, + sc_carry_ceiling, // killough 4/11/98: carry objects hanging on ceilings + } type; // Type of scroll effect +} scroll_t; + +// phares 3/12/98: added new model of friction for ice/sludge effects + +typedef struct { + thinker_t thinker; // Thinker structure for friction + int friction; // friction value (E800 = normal) + int movefactor; // inertia factor when adding to momentum + int affectee; // Number of affected sector +} friction_t; + +// phares 3/20/98: added new model of Pushers for push/pull effects + +typedef struct { + thinker_t thinker; // Thinker structure for Pusher + enum + { + p_push, + p_pull, + p_wind, + p_current, + } type; + mobj_t* source; // Point source if point pusher + int x_mag; // X Strength + int y_mag; // Y Strength + int magnitude; // Vector strength for point pusher + int radius; // Effective radius for point pusher + int x; // X of point source if point pusher + int y; // Y of point source if point pusher + int affectee; // Number of affected sector +} pusher_t; + +////////////////////////////////////////////////////////////////// +// +// external data declarations +// +////////////////////////////////////////////////////////////////// + +// list of retriggerable buttons active +extern button_t buttonlist[MAXBUTTONS]; + +extern platlist_t *activeplats; // killough 2/14/98 + +extern ceilinglist_t *activeceilings; // jff 2/22/98 + +//////////////////////////////////////////////////////////////// +// +// Linedef and sector special utility function prototypes +// +//////////////////////////////////////////////////////////////// + +int twoSided +( int sector, + int line ); + +sector_t* getSector +( int currentSector, + int line, + int side ); + +side_t* getSide +( int currentSector, + int line, + int side ); + +fixed_t P_FindLowestFloorSurrounding +( sector_t* sec ); + +fixed_t P_FindHighestFloorSurrounding +( sector_t* sec ); + +fixed_t P_FindNextHighestFloor +( sector_t* sec, + int currentheight ); + +fixed_t P_FindNextLowestFloor +( sector_t* sec, + int currentheight ); + +fixed_t P_FindLowestCeilingSurrounding +( sector_t* sec ); // jff 2/04/98 + +fixed_t P_FindHighestCeilingSurrounding +( sector_t* sec ); // jff 2/04/98 + +fixed_t P_FindNextLowestCeiling +( sector_t *sec, + int currentheight ); // jff 2/04/98 + +fixed_t P_FindNextHighestCeiling +( sector_t *sec, + int currentheight ); // jff 2/04/98 + +fixed_t P_FindShortestTextureAround +( int secnum ); // jff 2/04/98 + +fixed_t P_FindShortestUpperAround +( int secnum ); // jff 2/04/98 + +sector_t* P_FindModelFloorSector +( fixed_t floordestheight, + int secnum ); //jff 02/04/98 + +sector_t* P_FindModelCeilingSector +( fixed_t ceildestheight, + int secnum ); //jff 02/04/98 + +int P_FindSectorFromLineTag +( const line_t *line, + int start ); // killough 4/17/98 + +int P_FindLineFromLineTag +( const line_t *line, + int start ); // killough 4/17/98 + +int P_FindMinSurroundingLight +( sector_t* sector, + int max ); + +sector_t* getNextSector +( line_t* line, + sector_t* sec ); + +int P_CheckTag +(line_t *line); // jff 2/27/98 + +dboolean P_CanUnlockGenDoor +( line_t* line, + player_t* player); + +dboolean PUREFUNC P_SectorActive +( special_e t, + const sector_t* s ); + +dboolean PUREFUNC P_IsSecret +( const sector_t *sec ); + +dboolean PUREFUNC P_WasSecret +( const sector_t *sec ); + +void P_ChangeSwitchTexture +( line_t* line, + int useAgain ); + +//////////////////////////////////////////////////////////////// +// +// Linedef and sector special action function prototypes +// +//////////////////////////////////////////////////////////////// + +// p_lights + +void T_LightFlash +( lightflash_t* flash ); + +void T_StrobeFlash +( strobe_t* flash ); + +// jff 8/8/98 add missing thinker for flicker +void T_FireFlicker +( fireflicker_t* flick ); + +void T_Glow +( glow_t* g ); + +// p_plats + +void T_PlatRaise +( plat_t* plat ); + +// p_doors + +void T_VerticalDoor +( vldoor_t* door ); + +// p_ceilng + +void T_MoveCeiling +( ceiling_t* ceiling ); + +// p_floor + +result_e T_MovePlane +( sector_t* sector, + fixed_t speed, + fixed_t dest, + dboolean crush, + int floorOrCeiling, + int direction ); + +void T_MoveFloor +( floormove_t* floor ); + +void T_MoveElevator +( elevator_t* elevator ); + +// p_spec + +void T_Scroll +( scroll_t * ); // killough 3/7/98: scroll effect thinker + +void T_Friction +( friction_t * ); // phares 3/12/98: friction thinker + +void T_Pusher +( pusher_t * ); // phares 3/20/98: Push thinker + +//////////////////////////////////////////////////////////////// +// +// Linedef and sector special handler prototypes +// +//////////////////////////////////////////////////////////////// + +// p_telept + +int EV_Teleport +( line_t* line, + int side, + mobj_t* thing ); + +// killough 2/14/98: Add silent teleporter +int EV_SilentTeleport +( line_t* line, + int side, + mobj_t* thing ); + +// killough 1/31/98: Add silent line teleporter +int EV_SilentLineTeleport +( line_t* line, + int side, + mobj_t* thing, + dboolean reverse); + +// p_floor + +int +EV_DoElevator +( line_t* line, + elevator_e type ); + +int EV_BuildStairs +( line_t* line, + stair_e type ); + +int EV_DoFloor +( line_t* line, + floor_e floortype ); + +// p_ceilng + +int EV_DoCeiling +( line_t* line, + ceiling_e type ); + +int EV_CeilingCrushStop +( line_t* line ); + +// p_doors + +int EV_VerticalDoor +( line_t* line, + mobj_t* thing ); + +int EV_DoDoor +( line_t* line, + vldoor_e type ); + +int EV_DoLockedDoor +( line_t* line, + vldoor_e type, + mobj_t* thing ); + +// p_lights + +int EV_StartLightStrobing +( line_t* line ); + +int EV_TurnTagLightsOff +( line_t* line ); + +int EV_LightTurnOn +( line_t* line, + int bright ); + +int EV_LightTurnOnPartway(line_t* line, fixed_t level); // killough 10/10/98 + +// p_floor + +int EV_DoChange +( line_t* line, + change_e changetype ); + +int EV_DoDonut +( line_t* line ); + +// p_plats + +int EV_DoPlat +( line_t* line, + plattype_e type, + int amount ); + +int EV_StopPlat +( line_t* line ); + +// p_genlin + +int EV_DoGenFloor +( line_t* line ); + +int EV_DoGenCeiling +( line_t* line ); + +int EV_DoGenLift +( line_t* line ); + +int EV_DoGenStairs +( line_t* line ); + +int EV_DoGenCrusher +( line_t* line ); + +int EV_DoGenDoor +( line_t* line ); + +int EV_DoGenLockedDoor +( line_t* line ); + +//////////////////////////////////////////////////////////////// +// +// Linedef and sector special thinker spawning +// +//////////////////////////////////////////////////////////////// + +// at game start +void P_InitPicAnims +( void ); + +void P_InitSwitchList +( void ); + +// at map load +void P_SpawnSpecials +( void ); + +// every tic +void P_UpdateSpecials +( void ); + +// when needed +dboolean P_UseSpecialLine +( mobj_t* thing, + line_t* line, + int side, + dboolean noplayercheck); + +void P_ShootSpecialLine +( mobj_t* thing, + line_t* line ); + +void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing, dboolean noplayercheck); + +void P_PlayerInSpecialSector +( player_t* player ); + +// p_lights + +void P_SpawnFireFlicker +( sector_t* sector ); + +void P_SpawnLightFlash +( sector_t* sector ); + +void P_SpawnStrobeFlash +( sector_t* sector, + int fastOrSlow, + int inSync ); + +void P_SpawnGlowingLight +( sector_t* sector ); + +// p_plats + +void P_AddActivePlat +( plat_t* plat ); + +void P_RemoveActivePlat +( plat_t* plat ); + +void P_RemoveAllActivePlats +( void ); // killough + +void P_ActivateInStasis +( int tag ); + +// p_doors + +void P_SpawnDoorCloseIn30 +( sector_t* sec ); + +void P_SpawnDoorRaiseIn5Mins +( sector_t* sec, + int secnum ); + +// p_ceilng + +void P_RemoveActiveCeiling +( ceiling_t* ceiling ); //jff 2/22/98 + +void P_RemoveAllActiveCeilings +( void ); //jff 2/22/98 + +void P_AddActiveCeiling +( ceiling_t* c ); + +int P_ActivateInStasisCeiling +( line_t* line ); + +mobj_t* P_GetPushThing(int); // phares 3/23/98 + +#endif diff --git a/src/p_switch.c b/src/p_switch.c new file mode 100644 index 0000000..79ff3f6 --- /dev/null +++ b/src/p_switch.c @@ -0,0 +1,1210 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Switches, buttons. Two-state animation. Exits. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "w_wad.h" +#include "r_main.h" +#include "p_spec.h" +#include "g_game.h" +#include "s_sound.h" +#include "sounds.h" +#include "lprintf.h" +#include "e6y.h"//e6y + +// killough 2/8/98: Remove switch limit + +static int *switchlist; // killough +static int max_numswitches; // killough +static int numswitches; // killough + +button_t buttonlist[MAXBUTTONS]; + +// +// P_InitSwitchList() +// +// Only called at game initialization in order to list the set of switches +// and buttons known to the engine. This enables their texture to change +// when activated, and in the case of buttons, change back after a timeout. +// +// This routine modified to read its data from a predefined lump or +// PWAD lump called SWITCHES rather than a static table in this module to +// allow wad designers to insert or modify switches. +// +// Lump format is an array of byte packed switchlist_t structures, terminated +// by a structure with episode == -0. The lump can be generated from a +// text source file using SWANTBLS.EXE, distributed with the BOOM utils. +// The standard list of switches and animations is contained in the example +// source text file DEFSWANI.DAT also in the BOOM util distribution. +// +// Rewritten by Lee Killough to remove limit 2/8/98 +// +void P_InitSwitchList(void) +{ + int i, index = 0; + int episode = (gamemode == registered || gamemode==retail) ? + 2 : gamemode == commercial ? 3 : 1; + const switchlist_t *alphSwitchList; //jff 3/23/98 pointer to switch table + int lump = W_GetNumForName("SWITCHES"); // cph - new wad lump handling + + //jff 3/23/98 read the switch table from a predefined lump + alphSwitchList = (const switchlist_t *)W_CacheLumpNum(lump); + + for (i=0;;i++) + { + if (index+1 >= max_numswitches) + switchlist = realloc(switchlist, sizeof *switchlist * + (max_numswitches = max_numswitches ? max_numswitches*2 : 8)); + if (LittleShort(alphSwitchList[i].episode) <= episode) //jff 5/11/98 endianess + { + int texture1, texture2; + + if (!LittleShort(alphSwitchList[i].episode)) + break; + + // Ignore switches referencing unknown texture names, instead of exiting. + // Warn if either one is missing, but only add if both are valid. + texture1 = R_CheckTextureNumForName(alphSwitchList[i].name1); + if (texture1 == -1) + lprintf(LO_WARN, "P_InitSwitchList: unknown texture %s\n", + alphSwitchList[i].name1); + texture2 = R_CheckTextureNumForName(alphSwitchList[i].name2); + if (texture2 == -1) + lprintf(LO_WARN, "P_InitSwitchList: unknown texture %s\n", + alphSwitchList[i].name2); + if (texture1 != -1 && texture2 != -1) { + switchlist[index++] = texture1; + switchlist[index++] = texture2; + } + } + } + + numswitches = index/2; + switchlist[index] = -1; + W_UnlockLumpNum(lump); +} + +// +// P_StartButton() +// +// Start a button (retriggerable switch) counting down till it turns off. +// +// Passed the linedef the button is on, which texture on the sidedef contains +// the button, the texture number of the button, and the time the button is +// to remain active in gametics. +// No return. +// +static void P_StartButton +( line_t* line, + bwhere_e w, + int texture, + int time ) +{ + int i; + + // See if button is already pressed + for (i = 0;i < MAXBUTTONS;i++) + if (buttonlist[i].btimer && buttonlist[i].line == line) + return; + + for (i = 0;i < MAXBUTTONS;i++) + if (!buttonlist[i].btimer) // use first unused element of list + { + buttonlist[i].line = line; + buttonlist[i].where = w; + buttonlist[i].btexture = texture; + buttonlist[i].btimer = time; + /* use sound origin of line itself - no need to compatibility-wrap + * as the popout code gets it wrong whatever its value */ + buttonlist[i].soundorg = (mobj_t *)&line->soundorg; + return; + } + + I_Error("P_StartButton: no button slots left!"); +} + +// +// P_ChangeSwitchTexture() +// +// Function that changes switch wall texture on activation. +// +// Passed the line which the switch is on, and whether its retriggerable. +// If not retriggerable, this function clears the line special to insure that +// +// No return +// +void P_ChangeSwitchTexture +( line_t* line, + int useAgain ) +{ + /* Rearranged a bit to avoid too much code duplication */ + mobj_t *soundorg; + int i, sound; + short *texture, *ttop, *tmid, *tbot; + bwhere_e position; + + ttop = &sides[line->sidenum[0]].toptexture; + tmid = &sides[line->sidenum[0]].midtexture; + tbot = &sides[line->sidenum[0]].bottomtexture; + + sound = sfx_swtchn; + /* use the sound origin of the linedef (its midpoint) + * unless in a compatibility mode */ + soundorg = (mobj_t *)&line->soundorg; + if (default_comp[comp_sound] || compatibility_level < prboom_6_compatibility) { + /* usually NULL, unless there is another button already pressed in, + * in which case it's the sound origin of that button press... */ + soundorg = buttonlist->soundorg; + } else { + // EXIT SWITCH? + /* don't do this unless you're in a compatibility mode */ + // proff - this works as advertised, but I don't like the sound + // if (line->special == 11 || line->special == 51) // exit or secret exit + // sound = sfx_swtchx; + } + + /* don't zero line->special until after exit switch test */ + if (!useAgain) + line->special = 0; + + /* search for a texture to change */ + texture = NULL; position = 0; + for (i = 0;i < numswitches*2;i++) { /* this could be more efficient... */ + if (switchlist[i] == *ttop) { + texture = ttop; position = top; break; + } else if (switchlist[i] == *tmid) { + texture = tmid; position = middle; break; + } else if (switchlist[i] == *tbot) { + texture = tbot; position = bottom; break; + } + } + if (texture == NULL) + return; /* no switch texture was found to change */ + *texture = switchlist[i^1]; + + S_StartSound(soundorg, sound); + + if (useAgain) + P_StartButton(line, position, switchlist[i], BUTTONTIME); +} + +// +// GetPairForSwitchTexture +// +int GetPairForSwitchTexture(side_t *side) +{ + int i; + short *texture, *ttop, *tmid, *tbot; + + ttop = &side->toptexture; + tmid = &side->midtexture; + tbot = &side->bottomtexture; + + /* search for a texture to change */ + texture = NULL; + for (i = 0;i < numswitches*2;i++) { + if (switchlist[i] == *ttop) { + texture = ttop; break; + } else if (switchlist[i] == *tmid) { + texture = tmid; break; + } else if (switchlist[i] == *tbot) { + texture = tbot; break; + } + } + + if (texture == NULL) + return -1; + + return switchlist[i^1]; +} + +// +// P_UseSpecialLine +// +// +// Called when a thing uses (pushes) a special line. +// Only the front sides of lines are usable. +// Dispatches to the appropriate linedef function handler. +// +// Passed the thing using the line, the line being used, and the side used +// Returns true if a thinker was created +// +dboolean +P_UseSpecialLine +( mobj_t* thing, + line_t* line, + int side, + dboolean bossaction) +{ + + // e6y + // b.m. side test was broken in boom201 + if ((demoplayback ? (demover != 201) : (compatibility_level != boom_201_compatibility))) + if (side) //jff 6/1/98 fix inadvertent deletion of side test + return false; + + //jff 02/04/98 add check here for generalized floor/ceil mover + if (!demo_compatibility) + { + // pointer to line function is NULL by default, set non-null if + // line special is push or switch generalized linedef type + int (*linefunc)(line_t *line)=NULL; + + // check each range of generalized linedefs + if ((unsigned)line->special >= GenEnd) + { + // Out of range for GenFloors + } + else if ((unsigned)line->special >= GenFloorBase) + { + if (!thing->player && !bossaction) + if ((line->special & FloorChange) || !(line->special & FloorModel)) + return false; // FloorModel is "Allow Monsters" if FloorChange is 0 + if (!comperr(comperr_zerotag) && !line->tag && ((line->special&6)!=6)) //e6y //jff 2/27/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenFloor; + } + else if ((unsigned)line->special >= GenCeilingBase) + { + if (!thing->player && !bossaction) + if ((line->special & CeilingChange) || !(line->special & CeilingModel)) + return false; // CeilingModel is "Allow Monsters" if CeilingChange is 0 + if (!comperr(comperr_zerotag) && !line->tag && ((line->special&6)!=6)) //e6y //jff 2/27/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenCeiling; + } + else if ((unsigned)line->special >= GenDoorBase) + { + if (!thing->player && !bossaction) + { + if (!(line->special & DoorMonster)) + return false; // monsters disallowed from this door + if (line->flags & ML_SECRET) // they can't open secret doors either + return false; + } + if (!comperr(comperr_zerotag) && !line->tag && ((line->special&6)!=6)) //e6y //jff 3/2/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenDoor; + } + else if ((unsigned)line->special >= GenLockedBase) + { + if (!thing->player || bossaction) + return false; // monsters disallowed from unlocking doors + if (!P_CanUnlockGenDoor(line,thing->player)) + return false; + if (!comperr(comperr_zerotag) && !line->tag && ((line->special&6)!=6)) //e6y //jff 2/27/98 all non-manual + return false; // generalized types require tag + + linefunc = EV_DoGenLockedDoor; + } + else if ((unsigned)line->special >= GenLiftBase) + { + if (!thing->player && !bossaction) + if (!(line->special & LiftMonster)) + return false; // monsters disallowed + if (!comperr(comperr_zerotag) && !line->tag && ((line->special&6)!=6)) //e6y //jff 2/27/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenLift; + } + else if ((unsigned)line->special >= GenStairsBase) + { + if (!thing->player && !bossaction) + if (!(line->special & StairMonster)) + return false; // monsters disallowed + if (!comperr(comperr_zerotag) && !line->tag && ((line->special&6)!=6)) //e6y //jff 2/27/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenStairs; + } + else if ((unsigned)line->special >= GenCrusherBase) + { + if (!thing->player && !bossaction) + if (!(line->special & CrusherMonster)) + return false; // monsters disallowed + if (!comperr(comperr_zerotag) && !line->tag && ((line->special&6)!=6)) //e6y //jff 2/27/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenCrusher; + } + + if (linefunc) + switch((line->special & TriggerType) >> TriggerTypeShift) + { + case PushOnce: + if (!side) + if (linefunc(line)) + line->special = 0; + return true; + case PushMany: + if (!side) + linefunc(line); + return true; + case SwitchOnce: + if (linefunc(line)) + P_ChangeSwitchTexture(line,0); + return true; + case SwitchMany: + if (linefunc(line)) + P_ChangeSwitchTexture(line,1); + return true; + default: // if not a switch/push type, do nothing here + return false; + } + } + + // Switches that other things can activate. + if (!thing->player && !bossaction) + { + // 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 + //jff 3/5/98 add ability to use teleporters for monsters + case 195: // switch teleporters + case 174: + case 210: // silent switch teleporters + case 209: + break; + + default: + return false; + break; + } + } + + if (bossaction) + { + switch(line->special) + { + // 0-tag specials, locked switches and teleporters need to be blocked for boss actions. + case 1: // MANUAL DOOR RAISE + case 32: // MANUAL BLUE + case 33: // MANUAL RED + case 34: // MANUAL YELLOW + case 117: // Blazing door raise + case 118: // Blazing door open + case 133: // BlzOpenDoor BLUE + case 135: // BlzOpenDoor RED + case 137: // BlzOpenDoor YEL + + case 99: // BlzOpenDoor BLUE + case 134: // BlzOpenDoor RED + case 136: // BlzOpenDoor YELLOW + + //jff 3/5/98 add ability to use teleporters for monsters + case 195: // switch teleporters + case 174: + case 210: // silent switch teleporters + case 209: + return false; + break; + } + } + + if (!P_CheckTag(line)) //jff 2/27/98 disallow zero tag on some types + return false; + + // Dispatch to handler according to linedef type + switch (line->special) + { + // Manual doors, push type with no tag + case 1: // Vertical Door + case 26: // Blue Door/Locked + case 27: // Yellow Door /Locked + case 28: // Red Door /Locked + + case 31: // Manual door open + case 32: // Blue locked door open + case 33: // Red locked door open + case 34: // Yellow locked door open + + case 117: // Blazing door raise + case 118: // Blazing door open + EV_VerticalDoor (line, thing); + break; + + // Switches (non-retriggerable) + case 7: + // Build Stairs + if (EV_BuildStairs(line,build8)) + P_ChangeSwitchTexture(line,0); + break; + + case 9: + // Change Donut + if (EV_DoDonut(line)) + P_ChangeSwitchTexture(line,0); + break; + + case 11: + /* Exit level + * killough 10/98: prevent zombies from exiting levels + */ + if (!bossaction && thing->player && thing->player->health <= 0 && !comp[comp_zombie]) + { + S_StartSound(thing, sfx_noway); + return false; + } + + P_ChangeSwitchTexture(line,0); + G_ExitLevel (); + break; + + case 14: + // Raise Floor 32 and change texture + if (EV_DoPlat(line,raiseAndChange,32)) + P_ChangeSwitchTexture(line,0); + break; + + case 15: + // Raise Floor 24 and change texture + if (EV_DoPlat(line,raiseAndChange,24)) + P_ChangeSwitchTexture(line,0); + break; + + case 18: + // Raise Floor to next highest floor + if (EV_DoFloor(line, raiseFloorToNearest)) + P_ChangeSwitchTexture(line,0); + break; + + case 20: + // Raise Plat next highest floor and change texture + if (EV_DoPlat(line,raiseToNearestAndChange,0)) + P_ChangeSwitchTexture(line,0); + break; + + case 21: + // PlatDownWaitUpStay + if (EV_DoPlat(line,downWaitUpStay,0)) + P_ChangeSwitchTexture(line,0); + break; + + case 23: + // Lower Floor to Lowest + if (EV_DoFloor(line,lowerFloorToLowest)) + P_ChangeSwitchTexture(line,0); + break; + + case 29: + // Raise Door + if (EV_DoDoor(line,normal)) + P_ChangeSwitchTexture(line,0); + break; + + case 41: + // Lower Ceiling to Floor + if (EV_DoCeiling(line,lowerToFloor)) + P_ChangeSwitchTexture(line,0); + break; + + case 71: + // Turbo Lower Floor + if (EV_DoFloor(line,turboLower)) + P_ChangeSwitchTexture(line,0); + break; + + case 49: + // Ceiling Crush And Raise + if (EV_DoCeiling(line,crushAndRaise)) + P_ChangeSwitchTexture(line,0); + break; + + case 50: + // Close Door + if (EV_DoDoor(line,closeDoor)) + P_ChangeSwitchTexture(line,0); + break; + + case 51: + /* Secret EXIT + * killough 10/98: prevent zombies from exiting levels + */ + if (!bossaction && thing->player && thing->player->health <= 0 && !comp[comp_zombie]) + { + S_StartSound(thing, sfx_noway); + return false; + } + + P_ChangeSwitchTexture(line,0); + G_SecretExitLevel (); + break; + + case 55: + // Raise Floor Crush + if (EV_DoFloor(line,raiseFloorCrush)) + P_ChangeSwitchTexture(line,0); + break; + + case 101: + // Raise Floor + if (EV_DoFloor(line,raiseFloor)) + P_ChangeSwitchTexture(line,0); + break; + + case 102: + // Lower Floor to Surrounding floor height + if (EV_DoFloor(line,lowerFloor)) + P_ChangeSwitchTexture(line,0); + break; + + case 103: + // Open Door + if (EV_DoDoor(line,openDoor)) + P_ChangeSwitchTexture(line,0); + break; + + case 111: + // Blazing Door Raise (faster than TURBO!) + if (EV_DoDoor (line,blazeRaise)) + P_ChangeSwitchTexture(line,0); + break; + + case 112: + // Blazing Door Open (faster than TURBO!) + if (EV_DoDoor (line,blazeOpen)) + P_ChangeSwitchTexture(line,0); + break; + + case 113: + // Blazing Door Close (faster than TURBO!) + if (EV_DoDoor (line,blazeClose)) + P_ChangeSwitchTexture(line,0); + break; + + case 122: + // Blazing PlatDownWaitUpStay + if (EV_DoPlat(line,blazeDWUS,0)) + P_ChangeSwitchTexture(line,0); + break; + + case 127: + // Build Stairs Turbo 16 + if (EV_BuildStairs(line,turbo16)) + P_ChangeSwitchTexture(line,0); + break; + + case 131: + // Raise Floor Turbo + if (EV_DoFloor(line,raiseFloorTurbo)) + P_ChangeSwitchTexture(line,0); + break; + + case 133: + // BlzOpenDoor BLUE + case 135: + // BlzOpenDoor RED + case 137: + // BlzOpenDoor YELLOW + if (EV_DoLockedDoor (line,blazeOpen,thing)) + P_ChangeSwitchTexture(line,0); + break; + + case 140: + // Raise Floor 512 + if (EV_DoFloor(line,raiseFloor512)) + P_ChangeSwitchTexture(line,0); + break; + + // killough 1/31/98: factored out compatibility check; + // added inner switch, relaxed check to demo_compatibility + + default: + if (!demo_compatibility) + switch (line->special) + { + //jff 1/29/98 added linedef types to fill all functions out so that + // all possess SR, S1, WR, W1 types + + case 158: + // Raise Floor to shortest lower texture + // 158 S1 EV_DoFloor(raiseToTexture), CSW(0) + if (EV_DoFloor(line,raiseToTexture)) + P_ChangeSwitchTexture(line,0); + break; + + case 159: + // Raise Floor to shortest lower texture + // 159 S1 EV_DoFloor(lowerAndChange) + if (EV_DoFloor(line,lowerAndChange)) + P_ChangeSwitchTexture(line,0); + break; + + case 160: + // Raise Floor 24 and change + // 160 S1 EV_DoFloor(raiseFloor24AndChange) + if (EV_DoFloor(line,raiseFloor24AndChange)) + P_ChangeSwitchTexture(line,0); + break; + + case 161: + // Raise Floor 24 + // 161 S1 EV_DoFloor(raiseFloor24) + if (EV_DoFloor(line,raiseFloor24)) + P_ChangeSwitchTexture(line,0); + break; + + case 162: + // Moving floor min n to max n + // 162 S1 EV_DoPlat(perpetualRaise,0) + if (EV_DoPlat(line,perpetualRaise,0)) + P_ChangeSwitchTexture(line,0); + break; + + case 163: + // Stop Moving floor + // 163 S1 EV_DoPlat(perpetualRaise,0) + EV_StopPlat(line); + P_ChangeSwitchTexture(line,0); + break; + + case 164: + // Start fast crusher + // 164 S1 EV_DoCeiling(fastCrushAndRaise) + if (EV_DoCeiling(line,fastCrushAndRaise)) + P_ChangeSwitchTexture(line,0); + break; + + case 165: + // Start slow silent crusher + // 165 S1 EV_DoCeiling(silentCrushAndRaise) + if (EV_DoCeiling(line,silentCrushAndRaise)) + P_ChangeSwitchTexture(line,0); + break; + + case 166: + // Raise ceiling, Lower floor + // 166 S1 EV_DoCeiling(raiseToHighest), EV_DoFloor(lowerFloortoLowest) + if (EV_DoCeiling(line, raiseToHighest) || + EV_DoFloor(line, lowerFloorToLowest)) + P_ChangeSwitchTexture(line,0); + break; + + case 167: + // Lower floor and Crush + // 167 S1 EV_DoCeiling(lowerAndCrush) + if (EV_DoCeiling(line, lowerAndCrush)) + P_ChangeSwitchTexture(line,0); + break; + + case 168: + // Stop crusher + // 168 S1 EV_CeilingCrushStop() + if (EV_CeilingCrushStop(line)) + P_ChangeSwitchTexture(line,0); + break; + + case 169: + // Lights to brightest neighbor sector + // 169 S1 EV_LightTurnOn(0) + EV_LightTurnOn(line,0); + P_ChangeSwitchTexture(line,0); + break; + + case 170: + // Lights to near dark + // 170 S1 EV_LightTurnOn(35) + EV_LightTurnOn(line,35); + P_ChangeSwitchTexture(line,0); + break; + + case 171: + // Lights on full + // 171 S1 EV_LightTurnOn(255) + EV_LightTurnOn(line,255); + P_ChangeSwitchTexture(line,0); + break; + + case 172: + // Start Lights Strobing + // 172 S1 EV_StartLightStrobing() + EV_StartLightStrobing(line); + P_ChangeSwitchTexture(line,0); + break; + + case 173: + // Lights to Dimmest Near + // 173 S1 EV_TurnTagLightsOff() + EV_TurnTagLightsOff(line); + P_ChangeSwitchTexture(line,0); + break; + + case 174: + // Teleport + // 174 S1 EV_Teleport(side,thing) + if (EV_Teleport(line,side,thing)) + P_ChangeSwitchTexture(line,0); + break; + + case 175: + // Close Door, Open in 30 secs + // 175 S1 EV_DoDoor(close30ThenOpen) + if (EV_DoDoor(line,close30ThenOpen)) + P_ChangeSwitchTexture(line,0); + break; + + case 189: //jff 3/15/98 create texture change no motion type + // Texture Change Only (Trigger) + // 189 S1 Change Texture/Type Only + if (EV_DoChange(line,trigChangeOnly)) + P_ChangeSwitchTexture(line,0); + break; + + case 203: + // Lower ceiling to lowest surrounding ceiling + // 203 S1 EV_DoCeiling(lowerToLowest) + if (EV_DoCeiling(line,lowerToLowest)) + P_ChangeSwitchTexture(line,0); + break; + + case 204: + // Lower ceiling to highest surrounding floor + // 204 S1 EV_DoCeiling(lowerToMaxFloor) + if (EV_DoCeiling(line,lowerToMaxFloor)) + P_ChangeSwitchTexture(line,0); + break; + + case 209: + // killough 1/31/98: silent teleporter + //jff 209 S1 SilentTeleport + if (EV_SilentTeleport(line, side, thing)) + P_ChangeSwitchTexture(line,0); + break; + + case 241: //jff 3/15/98 create texture change no motion type + // Texture Change Only (Numeric) + // 241 S1 Change Texture/Type Only + if (EV_DoChange(line,numChangeOnly)) + P_ChangeSwitchTexture(line,0); + break; + + case 221: + // Lower floor to next lowest floor + // 221 S1 Lower Floor To Nearest Floor + if (EV_DoFloor(line,lowerFloorToNearest)) + P_ChangeSwitchTexture(line,0); + break; + + case 229: + // Raise elevator next floor + // 229 S1 Raise Elevator next floor + if (EV_DoElevator(line,elevateUp)) + P_ChangeSwitchTexture(line,0); + break; + + case 233: + // Lower elevator next floor + // 233 S1 Lower Elevator next floor + if (EV_DoElevator(line,elevateDown)) + P_ChangeSwitchTexture(line,0); + break; + + case 237: + // Elevator to current floor + // 237 S1 Elevator to current floor + if (EV_DoElevator(line,elevateCurrent)) + P_ChangeSwitchTexture(line,0); + break; + + + // jff 1/29/98 end of added S1 linedef types + + //jff 1/29/98 added linedef types to fill all functions out so that + // all possess SR, S1, WR, W1 types + + case 78: //jff 3/15/98 create texture change no motion type + // Texture Change Only (Numeric) + // 78 SR Change Texture/Type Only + if (EV_DoChange(line,numChangeOnly)) + P_ChangeSwitchTexture(line,1); + break; + + case 176: + // Raise Floor to shortest lower texture + // 176 SR EV_DoFloor(raiseToTexture), CSW(1) + if (EV_DoFloor(line,raiseToTexture)) + P_ChangeSwitchTexture(line,1); + break; + + case 177: + // Raise Floor to shortest lower texture + // 177 SR EV_DoFloor(lowerAndChange) + if (EV_DoFloor(line,lowerAndChange)) + P_ChangeSwitchTexture(line,1); + break; + + case 178: + // Raise Floor 512 + // 178 SR EV_DoFloor(raiseFloor512) + if (EV_DoFloor(line,raiseFloor512)) + P_ChangeSwitchTexture(line,1); + break; + + case 179: + // Raise Floor 24 and change + // 179 SR EV_DoFloor(raiseFloor24AndChange) + if (EV_DoFloor(line,raiseFloor24AndChange)) + P_ChangeSwitchTexture(line,1); + break; + + case 180: + // Raise Floor 24 + // 180 SR EV_DoFloor(raiseFloor24) + if (EV_DoFloor(line,raiseFloor24)) + P_ChangeSwitchTexture(line,1); + break; + + case 181: + // Moving floor min n to max n + // 181 SR EV_DoPlat(perpetualRaise,0) + + EV_DoPlat(line,perpetualRaise,0); + P_ChangeSwitchTexture(line,1); + break; + + case 182: + // Stop Moving floor + // 182 SR EV_DoPlat(perpetualRaise,0) + EV_StopPlat(line); + P_ChangeSwitchTexture(line,1); + break; + + case 183: + // Start fast crusher + // 183 SR EV_DoCeiling(fastCrushAndRaise) + if (EV_DoCeiling(line,fastCrushAndRaise)) + P_ChangeSwitchTexture(line,1); + break; + + case 184: + // Start slow crusher + // 184 SR EV_DoCeiling(crushAndRaise) + if (EV_DoCeiling(line,crushAndRaise)) + P_ChangeSwitchTexture(line,1); + break; + + case 185: + // Start slow silent crusher + // 185 SR EV_DoCeiling(silentCrushAndRaise) + if (EV_DoCeiling(line,silentCrushAndRaise)) + P_ChangeSwitchTexture(line,1); + break; + + case 186: + // Raise ceiling, Lower floor + // 186 SR EV_DoCeiling(raiseToHighest), EV_DoFloor(lowerFloortoLowest) + if (EV_DoCeiling(line, raiseToHighest) || + EV_DoFloor(line, lowerFloorToLowest)) + P_ChangeSwitchTexture(line,1); + break; + + case 187: + // Lower floor and Crush + // 187 SR EV_DoCeiling(lowerAndCrush) + if (EV_DoCeiling(line, lowerAndCrush)) + P_ChangeSwitchTexture(line,1); + break; + + case 188: + // Stop crusher + // 188 SR EV_CeilingCrushStop() + if (EV_CeilingCrushStop(line)) + P_ChangeSwitchTexture(line,1); + break; + + case 190: //jff 3/15/98 create texture change no motion type + // Texture Change Only (Trigger) + // 190 SR Change Texture/Type Only + if (EV_DoChange(line,trigChangeOnly)) + P_ChangeSwitchTexture(line,1); + break; + + case 191: + // Lower Pillar, Raise Donut + // 191 SR EV_DoDonut() + if (EV_DoDonut(line)) + P_ChangeSwitchTexture(line,1); + break; + + case 192: + // Lights to brightest neighbor sector + // 192 SR EV_LightTurnOn(0) + EV_LightTurnOn(line,0); + P_ChangeSwitchTexture(line,1); + break; + + case 193: + // Start Lights Strobing + // 193 SR EV_StartLightStrobing() + EV_StartLightStrobing(line); + P_ChangeSwitchTexture(line,1); + break; + + case 194: + // Lights to Dimmest Near + // 194 SR EV_TurnTagLightsOff() + EV_TurnTagLightsOff(line); + P_ChangeSwitchTexture(line,1); + break; + + case 195: + // Teleport + // 195 SR EV_Teleport(side,thing) + if (EV_Teleport(line,side,thing)) + P_ChangeSwitchTexture(line,1); + break; + + case 196: + // Close Door, Open in 30 secs + // 196 SR EV_DoDoor(close30ThenOpen) + if (EV_DoDoor(line,close30ThenOpen)) + P_ChangeSwitchTexture(line,1); + break; + + case 205: + // Lower ceiling to lowest surrounding ceiling + // 205 SR EV_DoCeiling(lowerToLowest) + if (EV_DoCeiling(line,lowerToLowest)) + P_ChangeSwitchTexture(line,1); + break; + + case 206: + // Lower ceiling to highest surrounding floor + // 206 SR EV_DoCeiling(lowerToMaxFloor) + if (EV_DoCeiling(line,lowerToMaxFloor)) + P_ChangeSwitchTexture(line,1); + break; + + case 210: + // killough 1/31/98: silent teleporter + //jff 210 SR SilentTeleport + if (EV_SilentTeleport(line, side, thing)) + P_ChangeSwitchTexture(line,1); + break; + + case 211: //jff 3/14/98 create instant toggle floor type + // Toggle Floor Between C and F Instantly + // 211 SR Toggle Floor Instant + if (EV_DoPlat(line,toggleUpDn,0)) + P_ChangeSwitchTexture(line,1); + break; + + case 222: + // Lower floor to next lowest floor + // 222 SR Lower Floor To Nearest Floor + if (EV_DoFloor(line,lowerFloorToNearest)) + P_ChangeSwitchTexture(line,1); + break; + + case 230: + // Raise elevator next floor + // 230 SR Raise Elevator next floor + if (EV_DoElevator(line,elevateUp)) + P_ChangeSwitchTexture(line,1); + break; + + case 234: + // Lower elevator next floor + // 234 SR Lower Elevator next floor + if (EV_DoElevator(line,elevateDown)) + P_ChangeSwitchTexture(line,1); + break; + + case 238: + // Elevator to current floor + // 238 SR Elevator to current floor + if (EV_DoElevator(line,elevateCurrent)) + P_ChangeSwitchTexture(line,1); + break; + + case 258: + // Build stairs, step 8 + // 258 SR EV_BuildStairs(build8) + if (EV_BuildStairs(line,build8)) + P_ChangeSwitchTexture(line,1); + break; + + case 259: + // Build stairs, step 16 + // 259 SR EV_BuildStairs(turbo16) + if (EV_BuildStairs(line,turbo16)) + P_ChangeSwitchTexture(line,1); + break; + + // 1/29/98 jff end of added SR linedef types + + } + break; + + // Buttons (retriggerable switches) + case 42: + // Close Door + if (EV_DoDoor(line,closeDoor)) + P_ChangeSwitchTexture(line,1); + break; + + case 43: + // Lower Ceiling to Floor + if (EV_DoCeiling(line,lowerToFloor)) + P_ChangeSwitchTexture(line,1); + break; + + case 45: + // Lower Floor to Surrounding floor height + if (EV_DoFloor(line,lowerFloor)) + P_ChangeSwitchTexture(line,1); + break; + + case 60: + // Lower Floor to Lowest + if (EV_DoFloor(line,lowerFloorToLowest)) + P_ChangeSwitchTexture(line,1); + break; + + case 61: + // Open Door + if (EV_DoDoor(line,openDoor)) + P_ChangeSwitchTexture(line,1); + break; + + case 62: + // PlatDownWaitUpStay + if (EV_DoPlat(line,downWaitUpStay,1)) + P_ChangeSwitchTexture(line,1); + break; + + case 63: + // Raise Door + if (EV_DoDoor(line,normal)) + P_ChangeSwitchTexture(line,1); + break; + + case 64: + // Raise Floor to ceiling + if (EV_DoFloor(line,raiseFloor)) + P_ChangeSwitchTexture(line,1); + break; + + case 66: + // Raise Floor 24 and change texture + if (EV_DoPlat(line,raiseAndChange,24)) + P_ChangeSwitchTexture(line,1); + break; + + case 67: + // Raise Floor 32 and change texture + if (EV_DoPlat(line,raiseAndChange,32)) + P_ChangeSwitchTexture(line,1); + break; + + case 65: + // Raise Floor Crush + if (EV_DoFloor(line,raiseFloorCrush)) + P_ChangeSwitchTexture(line,1); + break; + + case 68: + // Raise Plat to next highest floor and change texture + if (EV_DoPlat(line,raiseToNearestAndChange,0)) + P_ChangeSwitchTexture(line,1); + break; + + case 69: + // Raise Floor to next highest floor + if (EV_DoFloor(line, raiseFloorToNearest)) + P_ChangeSwitchTexture(line,1); + break; + + case 70: + // Turbo Lower Floor + if (EV_DoFloor(line,turboLower)) + P_ChangeSwitchTexture(line,1); + break; + + case 114: + // Blazing Door Raise (faster than TURBO!) + if (EV_DoDoor (line,blazeRaise)) + P_ChangeSwitchTexture(line,1); + break; + + case 115: + // Blazing Door Open (faster than TURBO!) + if (EV_DoDoor (line,blazeOpen)) + P_ChangeSwitchTexture(line,1); + break; + + case 116: + // Blazing Door Close (faster than TURBO!) + if (EV_DoDoor (line,blazeClose)) + P_ChangeSwitchTexture(line,1); + break; + + case 123: + // Blazing PlatDownWaitUpStay + if (EV_DoPlat(line,blazeDWUS,0)) + P_ChangeSwitchTexture(line,1); + break; + + case 132: + // Raise Floor Turbo + if (EV_DoFloor(line,raiseFloorTurbo)) + P_ChangeSwitchTexture(line,1); + break; + + case 99: + // BlzOpenDoor BLUE + case 134: + // BlzOpenDoor RED + case 136: + // BlzOpenDoor YELLOW + if (EV_DoLockedDoor (line,blazeOpen,thing)) + P_ChangeSwitchTexture(line,1); + break; + + case 138: + // Light Turn On + EV_LightTurnOn(line,255); + P_ChangeSwitchTexture(line,1); + break; + + case 139: + // Light Turn Off + EV_LightTurnOn(line,35); + P_ChangeSwitchTexture(line,1); + break; + } + return true; +} diff --git a/src/p_telept.c b/src/p_telept.c new file mode 100644 index 0000000..c091e54 --- /dev/null +++ b/src/p_telept.c @@ -0,0 +1,348 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Teleportation. + * + *-----------------------------------------------------------------------------*/ + +#include "doomdef.h" +#include "doomstat.h" +#include "p_spec.h" +#include "p_maputl.h" +#include "p_map.h" +#include "r_main.h" +#include "p_tick.h" +#include "s_sound.h" +#include "sounds.h" +#include "p_user.h" +#include "r_demo.h" + +static mobj_t* P_TeleportDestination(line_t* line) +{ + int i; + for (i = -1; (i = P_FindSectorFromLineTag(line, i)) >= 0;) { + register thinker_t* th = NULL; + while ((th = P_NextThinker(th,th_misc)) != NULL) + if (th->function == P_MobjThinker) { + register mobj_t* m = (mobj_t*)th; + if (m->type == MT_TELEPORTMAN && + m->subsector->sector->iSectorID == i) + return m; + } + } + return NULL; +} +// +// TELEPORTATION +// +// killough 5/3/98: reformatted, cleaned up + +int EV_Teleport(line_t *line, int side, mobj_t *thing) +{ + mobj_t *m; + + // don't teleport missiles + // Don't teleport if hit back of line, + // so you can get out of teleporter. + if (side || thing->flags & MF_MISSILE) + return 0; + + // killough 1/31/98: improve performance by using + // P_FindSectorFromLineTag instead of simple linear search. + + if ((m = P_TeleportDestination(line)) != NULL) + { + fixed_t oldx = thing->x, oldy = thing->y, oldz = thing->z; + player_t *player = thing->player; + + // killough 5/12/98: exclude voodoo dolls: + if (player && player->mo != thing) + player = NULL; + + if (!P_TeleportMove(thing, m->x, m->y, false)) /* killough 8/9/98 */ + return 0; + + if (compatibility_level != finaldoom_compatibility) + thing->z = thing->floorz; + thing->PrevZ = thing->z; + + if (player) + player->viewz = thing->z + player->viewheight; + + // spawn teleport fog and emit sound at source + S_StartSound(P_SpawnMobj(oldx, oldy, oldz, MT_TFOG), sfx_telept); + + // spawn teleport fog and emit sound at destination + S_StartSound(P_SpawnMobj(m->x + + 20*finecosine[m->angle>>ANGLETOFINESHIFT], + m->y + + 20*finesine[m->angle>>ANGLETOFINESHIFT], + thing->z, MT_TFOG), + sfx_telept); + + /* don't move for a bit + * cph - DEMOSYNC - BOOM had (player) here? */ + if (thing->player) + thing->reactiontime = 18; + + thing->angle = m->angle; + + thing->momx = thing->momy = thing->momz = 0; + + /* killough 10/98: kill all bobbing momentum too */ + if (player) + player->momx = player->momy = 0; + + // e6y + if (player && player->mo == thing) + R_ResetAfterTeleport(player); + + return 1; + } + return 0; +} + +// +// Silent TELEPORTATION, by Lee Killough +// Primarily for rooms-over-rooms etc. +// + +int EV_SilentTeleport(line_t *line, int side, mobj_t *thing) +{ + mobj_t *m; + + // don't teleport missiles + // Don't teleport if hit back of line, + // so you can get out of teleporter. + + if (side || thing->flags & MF_MISSILE) + return 0; + + if ((m = P_TeleportDestination(line)) != NULL) + { + // Height of thing above ground, in case of mid-air teleports: + fixed_t z = thing->z - thing->floorz; + + // Get the angle between the exit thing and source linedef. + // Rotate 90 degrees, so that walking perpendicularly across + // teleporter linedef causes thing to exit in the direction + // indicated by the exit thing. + angle_t angle = + R_PointToAngle2(0, 0, line->dx, line->dy) - m->angle + ANG90; + + // Sine, cosine of angle adjustment + fixed_t s = finesine[angle>>ANGLETOFINESHIFT]; + fixed_t c = finecosine[angle>>ANGLETOFINESHIFT]; + + // Momentum of thing crossing teleporter linedef + fixed_t momx = thing->momx; + fixed_t momy = thing->momy; + + // Whether this is a player, and if so, a pointer to its player_t + player_t *player = thing->player; + + // Attempt to teleport, aborting if blocked + if (!P_TeleportMove(thing, m->x, m->y, false)) /* killough 8/9/98 */ + return 0; + + // Rotate thing according to difference in angles + thing->angle += angle; + + // Adjust z position to be same height above ground as before + thing->z = z + thing->floorz; + thing->PrevZ = thing->z; + + // Rotate thing's momentum to come out of exit just like it entered + thing->momx = FixedMul(momx, c) - FixedMul(momy, s); + thing->momy = FixedMul(momy, c) + FixedMul(momx, s); + + // Adjust player's view, in case there has been a height change + // Voodoo dolls are excluded by making sure player->mo == thing. + if (player && player->mo == thing) + { + // Save the current deltaviewheight, used in stepping + fixed_t deltaviewheight = player->deltaviewheight; + + // Clear deltaviewheight, since we don't want any changes + player->deltaviewheight = 0; + + // Set player's view according to the newly set parameters + P_CalcHeight(player); + + // Reset the delta to have the same dynamics as before + player->deltaviewheight = deltaviewheight; + } + + // e6y + if (player && player->mo == thing) + R_ResetAfterTeleport(player); + + return 1; + } + return 0; +} + +// +// Silent linedef-based TELEPORTATION, by Lee Killough +// Primarily for rooms-over-rooms etc. +// This is the complete player-preserving kind of teleporter. +// It has advantages over the teleporter with thing exits. +// + +// maximum fixed_t units to move object to avoid hiccups +#define FUDGEFACTOR 10 + +int EV_SilentLineTeleport(line_t *line, int side, mobj_t *thing, + dboolean reverse) +{ + int i; + line_t *l; + + if (side || thing->flags & MF_MISSILE) + return 0; + + for (i = -1; (i = P_FindLineFromLineTag(line, i)) >= 0;) + if ((l=lines+i) != line && l->backsector) + { + // Get the thing's position along the source linedef + fixed_t pos = D_abs(line->dx) > D_abs(line->dy) ? + FixedDiv(thing->x - line->v1->x, line->dx) : + FixedDiv(thing->y - line->v1->y, line->dy) ; + + // Get the angle between the two linedefs, for rotating + // orientation and momentum. Rotate 180 degrees, and flip + // the position across the exit linedef, if reversed. + angle_t angle = (reverse ? pos = FRACUNIT-pos, 0 : ANG180) + + R_PointToAngle2(0, 0, l->dx, l->dy) - + R_PointToAngle2(0, 0, line->dx, line->dy); + + // Interpolate position across the exit linedef + fixed_t x = l->v2->x - FixedMul(pos, l->dx); + fixed_t y = l->v2->y - FixedMul(pos, l->dy); + + // Sine, cosine of angle adjustment + fixed_t s = finesine[angle>>ANGLETOFINESHIFT]; + fixed_t c = finecosine[angle>>ANGLETOFINESHIFT]; + + // Maximum distance thing can be moved away from interpolated + // exit, to ensure that it is on the correct side of exit linedef + int fudge = FUDGEFACTOR; + + // Whether this is a player, and if so, a pointer to its player_t. + // Voodoo dolls are excluded by making sure thing->player->mo==thing. + player_t *player = thing->player && thing->player->mo == thing ? + thing->player : NULL; + + // Whether walking towards first side of exit linedef steps down + int stepdown = + l->frontsector->floorheight < l->backsector->floorheight; + + // Height of thing above ground + fixed_t z = thing->z - thing->floorz; + + // Side to exit the linedef on positionally. + // + // Notes: + // + // This flag concerns exit position, not momentum. Due to + // roundoff error, the thing can land on either the left or + // the right side of the exit linedef, and steps must be + // taken to make sure it does not end up on the wrong side. + // + // Exit momentum is always towards side 1 in a reversed + // teleporter, and always towards side 0 otherwise. + // + // Exiting positionally on side 1 is always safe, as far + // as avoiding oscillations and stuck-in-wall problems, + // but may not be optimum for non-reversed teleporters. + // + // Exiting on side 0 can cause oscillations if momentum + // is towards side 1, as it is with reversed teleporters. + // + // Exiting on side 1 slightly improves player viewing + // when going down a step on a non-reversed teleporter. + + int side = reverse || (player && stepdown); + + // Make sure we are on correct side of exit linedef. + while (P_PointOnLineSide(x, y, l) != side && --fudge>=0) + if (D_abs(l->dx) > D_abs(l->dy)) + y -= (l->dx < 0) != side ? -1 : 1; + else + x += (l->dy < 0) != side ? -1 : 1; + + // Attempt to teleport, aborting if blocked + if (!P_TeleportMove(thing, x, y, false)) /* killough 8/9/98 */ + return 0; + + // e6y + if (player && player->mo == thing) + R_ResetAfterTeleport(player); + + // Adjust z position to be same height above ground as before. + // Ground level at the exit is measured as the higher of the + // two floor heights at the exit linedef. + thing->z = z + sides[l->sidenum[stepdown]].sector->floorheight; + thing->PrevZ = thing->z; + + // Rotate thing's orientation according to difference in linedef angles + thing->angle += angle; + + // Momentum of thing crossing teleporter linedef + x = thing->momx; + y = thing->momy; + + // Rotate thing's momentum to come out of exit just like it entered + thing->momx = FixedMul(x, c) - FixedMul(y, s); + thing->momy = FixedMul(y, c) + FixedMul(x, s); + + // Adjust a player's view, in case there has been a height change + if (player) + { + // Save the current deltaviewheight, used in stepping + fixed_t deltaviewheight = player->deltaviewheight; + + // Clear deltaviewheight, since we don't want any changes now + player->deltaviewheight = 0; + + // Set player's view according to the newly set parameters + P_CalcHeight(player); + + // Reset the delta to have the same dynamics as before + player->deltaviewheight = deltaviewheight; + } + + // e6y + if (player && player->mo == thing) + R_ResetAfterTeleport(player); + + return 1; + } + return 0; +} diff --git a/src/p_tick.c b/src/p_tick.c new file mode 100644 index 0000000..4edbec7 --- /dev/null +++ b/src/p_tick.c @@ -0,0 +1,299 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000,2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Thinker, Ticker. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "p_user.h" +#include "p_spec.h" +#include "p_tick.h" +#include "p_map.h" +#include "r_fps.h" +#include "e6y.h" +#include "s_advsound.h" + +int leveltime; + +static dboolean newthinkerpresent; + +// +// 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. +// + +// killough 8/29/98: we maintain several separate threads, each containing +// a special class of thinkers, to allow more efficient searches. +thinker_t thinkerclasscap[th_all+1]; + +// +// P_InitThinkers +// + +void P_InitThinkers(void) +{ + int i; + + for (i=0; ifunction == P_RemoveThinkerDelayed ? th_delete : + thinker->function == P_MobjThinker && + ((mobj_t *) thinker)->health > 0 && + (((mobj_t *) thinker)->flags & MF_COUNTKILL || + ((mobj_t *) thinker)->type == MT_SKULL) ? + ((mobj_t *) thinker)->flags & MF_FRIEND ? + th_friends : th_enemies : th_misc; + + { + /* Remove from current thread, if in one */ + if ((th = thinker->cnext)!= NULL) + (th->cprev = thinker->cprev)->cnext = th; + } + + // Add to appropriate thread + th = &thinkerclasscap[class]; + th->cprev->cnext = thinker; + thinker->cnext = th; + thinker->cprev = th->cprev; + th->cprev = thinker; +} + +// +// P_AddThinker +// Adds a new thinker at the end of the list. +// + +void P_AddThinker(thinker_t* thinker) +{ + thinkercap.prev->next = thinker; + thinker->next = &thinkercap; + thinker->prev = thinkercap.prev; + thinkercap.prev = thinker; + + thinker->references = 0; // killough 11/98: init reference counter to 0 + + // killough 8/29/98: set sentinel pointers, and then add to appropriate list + thinker->cnext = thinker->cprev = NULL; + P_UpdateThinker(thinker); + newthinkerpresent = true; +} + +// +// killough 11/98: +// +// Make currentthinker external, so that P_RemoveThinkerDelayed +// can adjust currentthinker when thinkers self-remove. + +static thinker_t *currentthinker; + +// +// P_RemoveThinkerDelayed() +// +// Called automatically as part of the thinker loop in P_RunThinkers(), +// on nodes which are pending deletion. +// +// If this thinker has no more pointers referencing it indirectly, +// remove it, and set currentthinker to one node preceeding it, so +// that the next step in P_RunThinkers() will get its successor. +// + +void P_RemoveThinkerDelayed(thinker_t *thinker) +{ + if (!thinker->references) + { + { /* Remove from main thinker list */ + thinker_t *next = thinker->next; + /* Note that currentthinker is guaranteed to point to us, + * and since we're freeing our memory, we had better change that. So + * point it to thinker->prev, so the iterator will correctly move on to + * thinker->prev->next = thinker->next */ + (next->prev = currentthinker = thinker->prev)->next = next; + } + { + /* Remove from current thinker class list */ + thinker_t *th = thinker->cnext; + (th->cprev = thinker->cprev)->cnext = th; + } + Z_Free(thinker); + } +} + +// +// P_RemoveThinker +// +// Deallocation is lazy -- it will not actually be freed +// until its thinking turn comes up. +// +// killough 4/25/98: +// +// Instead of marking the function with -1 value cast to a function pointer, +// set the function to P_RemoveThinkerDelayed(), so that later, it will be +// removed automatically as part of the thinker process. +// + +void P_RemoveThinker(thinker_t *thinker) +{ + R_StopInterpolationIfNeeded(thinker); + thinker->function = P_RemoveThinkerDelayed; + + P_UpdateThinker(thinker); +} + +/* cph 2002/01/13 - iterator for thinker list + * WARNING: Do not modify thinkers between calls to this functin + */ +thinker_t* P_NextThinker(thinker_t* th, th_class cl) +{ + thinker_t* top = &thinkerclasscap[cl]; + if (!th) th = top; + th = cl == th_all ? th->next : th->cnext; + return th == top ? NULL : th; +} + +/* + * P_SetTarget + * + * This function is used to keep track of pointer references to mobj thinkers. + * In Doom, objects such as lost souls could sometimes be removed despite + * their still being referenced. In Boom, 'target' mobj fields were tested + * during each gametic, and any objects pointed to by them would be prevented + * from being removed. But this was incomplete, and was slow (every mobj was + * checked during every gametic). Now, we keep a count of the number of + * references, and delay removal until the count is 0. + */ + +void P_SetTarget(mobj_t **mop, mobj_t *targ) +{ + if (*mop) // If there was a target already, decrease its refcount + (*mop)->thinker.references--; + if ((*mop = targ)) // Set new target and if non-NULL, increase its counter + targ->thinker.references++; +} + +// +// P_RunThinkers +// +// killough 4/25/98: +// +// Fix deallocator to stop using "next" pointer after node has been freed +// (a Doom bug). +// +// Process each thinker. For thinkers which are marked deleted, we must +// load the "next" pointer prior to freeing the node. In Doom, the "next" +// pointer was loaded AFTER the thinker was freed, which could have caused +// crashes. +// +// But if we are not deleting the thinker, we should reload the "next" +// pointer after calling the function, in case additional thinkers are +// added at the end of the list. +// +// killough 11/98: +// +// Rewritten to delete nodes implicitly, by making currentthinker +// external and using P_RemoveThinkerDelayed() implicitly. +// + +static void P_RunThinkers (void) +{ + for (currentthinker = thinkercap.next; + currentthinker != &thinkercap; + currentthinker = currentthinker->next) + { + if (newthinkerpresent) + R_ActivateThinkerInterpolations(currentthinker); + if (currentthinker->function) + currentthinker->function(currentthinker); + } + newthinkerpresent = false; + + // Dedicated thinkers + T_MAPMusic(); +} + +// +// P_Ticker +// + +void P_Ticker (void) +{ + int i; + + /* pause if in menu and at least one tic has been run + * + * killough 9/29/98: note that this ties in with basetic, + * since G_Ticker does the pausing during recording or + * playback, and compenates by incrementing basetic. + * + * All of this complicated mess is used to preserve demo sync. + */ + + if (paused || (menuactive && !demoplayback && !netgame && + players[consoleplayer].viewz != 1)) + { + P_ResetWalkcam(); + return; + } + + R_UpdateInterpolations (); + + P_MapStart(); + // not if this is an intermission screen + if(gamestate==GS_LEVEL) + for (i=0; i>= ANGLETOFINESHIFT; + + player->mo->momx += FixedMul(move,finecosine[angle]); + player->mo->momy += FixedMul(move,finesine[angle]); +} + +void P_Thrust(player_t* player,angle_t angle,fixed_t move) +{ + angle >>= ANGLETOFINESHIFT; + + if ((player->mo->flags & MF_FLY) && player->mo->pitch != 0) + { + angle_t pitch = player->mo->pitch >> ANGLETOFINESHIFT; + fixed_t zpush = FixedMul(move, finesine[pitch]); + player->mo->momz -= zpush; + move = FixedMul(move, finecosine[pitch]); + } + + player->mo->momx += FixedMul(move,finecosine[angle]); + player->mo->momy += FixedMul(move,finesine[angle]); +} + + +/* + * P_Bob + * Same as P_Thrust, but only affects bobbing. + * + * killough 10/98: We apply thrust separately between the real physical player + * and the part which affects bobbing. This way, bobbing only comes from player + * motion, nothing external, avoiding many problems, e.g. bobbing should not + * occur on conveyors, unless the player walks on one, and bobbing should be + * reduced at a regular rate, even on ice (where the player coasts). + */ + +static void P_Bob(player_t *player, angle_t angle, fixed_t move) +{ + //e6y + if (!mbf_features && !prboom_comp[PC_PRBOOM_FRICTION].state) + return; + + player->momx += FixedMul(move,finecosine[angle >>= ANGLETOFINESHIFT]); + player->momy += FixedMul(move,finesine[angle]); +} + +// +// P_CalcHeight +// Calculate the walking / running height adjustment +// + +void P_CalcHeight (player_t* player) +{ + int angle; + fixed_t bob; + + // Regular movement bobbing + // (needs to be calculated for gun swing + // even if not on ground) + // OPTIMIZE: tablify angle + // Note: a LUT allows for effects + // like a ramp with low health. + + + /* killough 10/98: Make bobbing depend only on player-applied motion. + * + * Note: don't reduce bobbing here if on ice: if you reduce bobbing here, + * it causes bobbing jerkiness when the player moves from ice to non-ice, + * and vice-versa. + */ + + player->bob = 0; + + if ((player->mo->flags & MF_FLY) && !onground) + { + player->bob = FRACUNIT / 2; + } + + if (mbf_features) + { + if (player_bobbing) + { + player->bob = (FixedMul(player->momx, player->momx) + + FixedMul(player->momy, player->momy)) >> 2; + } + } + else + { + if (demo_compatibility || player_bobbing || prboom_comp[PC_FORCE_INCORRECT_BOBBING_IN_BOOM].state) + { + player->bob = (FixedMul(player->mo->momx, player->mo->momx) + + FixedMul(player->mo->momy, player->mo->momy)) >> 2; + } + } + + //e6y + if (!prboom_comp[PC_PRBOOM_FRICTION].state && + compatibility_level >= boom_202_compatibility && + compatibility_level <= lxdoom_1_compatibility && + player->mo->friction > ORIG_FRICTION) // ice? + { + if (player->bob > (MAXBOB>>2)) + player->bob = MAXBOB>>2; + } + else + { + if (player->bob > MAXBOB) + player->bob = MAXBOB; + } + + if (!onground || player->cheats & CF_NOMOMENTUM) + { + player->viewz = player->mo->z + VIEWHEIGHT; + + if (player->viewz > player->mo->ceilingz-4*FRACUNIT) + player->viewz = player->mo->ceilingz-4*FRACUNIT; + +// The following line was in the Id source and appears // phares 2/25/98 +// to be a bug. player->viewz is checked in a similar +// manner at a different exit below. + +// player->viewz = player->mo->z + player->viewheight; + return; + } + + angle = (FINEANGLES/20*leveltime)&FINEMASK; + bob = FixedMul(player->bob/2,finesine[angle]); + + // move viewheight + + if (player->playerstate == PST_LIVE) + { + player->viewheight += player->deltaviewheight; + + if (player->viewheight > VIEWHEIGHT) + { + player->viewheight = VIEWHEIGHT; + player->deltaviewheight = 0; + } + + if (player->viewheight < VIEWHEIGHT/2) + { + player->viewheight = VIEWHEIGHT/2; + if (player->deltaviewheight <= 0) + player->deltaviewheight = 1; + } + + if (player->deltaviewheight) + { + player->deltaviewheight += FRACUNIT/4; + if (!player->deltaviewheight) + player->deltaviewheight = 1; + } + } + + player->viewz = player->mo->z + player->viewheight + bob; + + if (player->viewz > player->mo->ceilingz-4*FRACUNIT) + player->viewz = player->mo->ceilingz-4*FRACUNIT; +} + + +// +// P_SetPitch +// Mouse Look Stuff +// +void P_SetPitch(player_t *player) +{ + mobj_t *mo = player->mo; + + if (player == &players[consoleplayer]) + { + if (!(demoplayback || democontinue)) + { + if (GetMouseLook()) + { + if (!mo->reactiontime && (!(automapmode & am_active) || (automapmode & am_overlay))) + { + mo->pitch += (mlooky << 16); + CheckPitch((signed int *)&mo->pitch); + } + } + else + { + mo->pitch = 0; + } + + R_DemoEx_WriteMLook(mo->pitch); + } + else + { + mo->pitch = R_DemoEx_ReadMLook(); + CheckPitch((signed int *)&mo->pitch); + } + } + else + { + mo->pitch = 0; + } +} + +// +// P_MovePlayer +// +// Adds momentum if the player is not in the air +// +// killough 10/98: simplified + +void P_MovePlayer (player_t* player) +{ + ticcmd_t *cmd = &player->cmd; + mobj_t *mo = player->mo; + + mo->angle += cmd->angleturn << 16; + + if (demo_smoothturns && player == &players[displayplayer]) + { + R_SmoothPlaying_Add(cmd->angleturn << 16); + } + + onground = mo->z <= mo->floorz; + + if ((player->mo->flags & MF_FLY) && player == &players[consoleplayer] && upmove != 0) + { + mo->momz = upmove << 8; + } + + if (comperr(comperr_allowjump)) + { + if (upmove > 0 && onground && player == &players[consoleplayer] && !(player->mo->flags & MF_FLY)) + { + if (!player->jumpTics) + { + mo->momz = (7 + default_comperr[comperr_allowjump]) * FRACUNIT; + player->jumpTics = 18; + } + } + } + + // killough 10/98: + // + // We must apply thrust to the player and bobbing separately, to avoid + // anomalies. The thrust applied to bobbing is always the same strength on + // ice, because the player still "works just as hard" to move, while the + // thrust applied to the movement varies with 'movefactor'. + + //e6y + if ((!demo_compatibility && !mbf_features && !prboom_comp[PC_PRBOOM_FRICTION].state) || + (cmd->forwardmove | cmd->sidemove)) // killough 10/98 + { + if (onground || mo->flags & MF_BOUNCES || (mo->flags & MF_FLY)) // killough 8/9/98 + { + int friction, movefactor = P_GetMoveFactor(mo, &friction); + + // killough 11/98: + // On sludge, make bobbing depend on efficiency. + // On ice, make it depend on effort. + + int bobfactor = + friction < ORIG_FRICTION ? movefactor : ORIG_FRICTION_FACTOR; + + if (cmd->forwardmove) + { + P_Bob(player,mo->angle,cmd->forwardmove*bobfactor); + P_Thrust(player,mo->angle,cmd->forwardmove*movefactor); + } + + if (cmd->sidemove) + { + P_Bob(player,mo->angle-ANG90,cmd->sidemove*bobfactor); + P_SideThrust(player,mo->angle-ANG90,cmd->sidemove*movefactor); + } + } + else if (comperr(comperr_allowjump)) + { + if (!onground) + { + if (cmd->forwardmove) + { + P_Thrust(player, mo->angle, FRACUNIT >> 8); + } + if (cmd->sidemove) + { + P_Thrust(player, mo->angle, FRACUNIT >> 8); + } + } + } + if (mo->state == states+S_PLAY) + P_SetMobjState(mo,S_PLAY_RUN1); + } +} + +#define ANG5 (ANG90/18) + +// +// P_DeathThink +// Fall on your face when dying. +// Decrease POV height to floor height. +// + +void P_DeathThink (player_t* player) +{ + angle_t angle; + angle_t delta; + + P_MovePsprites (player); + + // fall to the ground + + onground = (player->mo->z <= player->mo->floorz); + if (player->mo->type == MT_GIBDTH) + { + // Flying bloody skull + player->viewheight = 6*FRACUNIT; + player->deltaviewheight = 0; + if (onground && (int)player->mo->pitch > -(int)ANG1*19) + { + player->mo->pitch -= ((int)ANG1*19 - player->mo->pitch) / 8; + } + } + else + { + if (player->viewheight > 6*FRACUNIT) + player->viewheight -= FRACUNIT; + + if (player->viewheight < 6*FRACUNIT) + player->viewheight = 6*FRACUNIT; + + player->deltaviewheight = 0; + } + + P_CalcHeight (player); + + if (player->attacker && player->attacker != player->mo) + { + angle = R_PointToAngle2 (player->mo->x, + player->mo->y, + player->attacker->x, + player->attacker->y); + + delta = angle - player->mo->angle; + + if (delta < ANG5 || delta > (unsigned)-ANG5) + { + // Looking at killer, + // so fade damage flash down. + + player->mo->angle = angle; + + if (player->damagecount) + player->damagecount--; + } + else if (delta < ANG180) + player->mo->angle += ANG5; + else + player->mo->angle -= ANG5; + } + else if (player->damagecount) + player->damagecount--; + + if (player->cmd.buttons & BT_USE) + player->playerstate = PST_REBORN; + R_SmoothPlaying_Reset(player); // e6y +} + + +// +// P_PlayerThink +// + +void P_PlayerThink (player_t* player) +{ + ticcmd_t* cmd; + weapontype_t newweapon; + + if (movement_smooth) + { + player->prev_viewz = player->viewz; + player->prev_viewangle = R_SmoothPlaying_Get(player) + viewangleoffset; + player->prev_viewpitch = player->mo->pitch + viewpitchoffset; + + if (&players[displayplayer] == player) + { + P_ResetWalkcam(); + } + } + + // killough 2/8/98, 3/21/98: + if (player->cheats & CF_NOCLIP) + player->mo->flags |= MF_NOCLIP; + else + player->mo->flags &= ~MF_NOCLIP; + + // chain saw run forward + + cmd = &player->cmd; + if (player->mo->flags & MF_JUSTATTACKED) + { + cmd->angleturn = 0; + cmd->forwardmove = 0xc800/512; + cmd->sidemove = 0; + player->mo->flags &= ~MF_JUSTATTACKED; + } + + if (player->playerstate == PST_DEAD) + { + P_DeathThink (player); + return; + } + + if (player->jumpTics) + { + player->jumpTics--; + } + // Move around. + // Reactiontime is used to prevent movement + // for a bit after a teleport. + + if (player->mo->reactiontime) + player->mo->reactiontime--; + else + P_MovePlayer (player); + + P_SetPitch(player); + + P_CalcHeight (player); // Determines view height and bobbing + + // Determine if there's anything about the sector you're in that's + // going to affect you, like painful floors. + + if (player->mo->subsector->sector->special) + P_PlayerInSpecialSector (player); + + // Check for weapon change. + + 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; + + // killough 3/22/98: For demo compatibility we must perform the fist + // and SSG weapons switches here, rather than in G_BuildTiccmd(). For + // other games which rely on user preferences, we must use the latter. + + if (demo_compatibility) + { // compatibility mode -- required for old demos -- killough + //e6y + if (!prboom_comp[PC_ALLOW_SSG_DIRECT].state) + newweapon = (cmd->buttons & BT_WEAPONMASK_OLD)>>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; + } + + // killough 2/8/98, 3/22/98 -- end of weapon selection changes + + if (player->weaponowned[newweapon] && newweapon != player->readyweapon) + + // Do not go to plasma or BFG in shareware, + // even if cheated. + + if ((newweapon != wp_plasma && newweapon != wp_bfg) + || (gamemode != shareware) ) + player->pendingweapon = newweapon; + } + + // check for use + + if (cmd->buttons & BT_USE) + { + if (!player->usedown) + { + P_UseLines (player); + player->usedown = true; + } + } + else + player->usedown = false; + + // cycle psprites + + P_MovePsprites (player); + + // Counters, time dependent power ups. + + // Strength counts up to diminish fade. + + if (player->powers[pw_strength]) + player->powers[pw_strength]++; + + // killough 1/98: Make idbeholdx toggle: + + if (player->powers[pw_invulnerability] > 0) // killough + player->powers[pw_invulnerability]--; + + if (player->powers[pw_invisibility] > 0) // killough + if (! --player->powers[pw_invisibility] ) + player->mo->flags &= ~MF_SHADOW; + + if (player->powers[pw_infrared] > 0) // killough + player->powers[pw_infrared]--; + + if (player->powers[pw_ironfeet] > 0) // killough + player->powers[pw_ironfeet]--; + + if (player->damagecount) + player->damagecount--; + + if (player->bonuscount) + player->bonuscount--; + + // Handling colormaps. + // killough 3/20/98: reformat to terse C syntax + player->fixedcolormap = palette_onpowers && + (player->powers[pw_invulnerability] > 4*32 || + player->powers[pw_invulnerability] & 8) ? INVERSECOLORMAP : + player->powers[pw_infrared] > 4*32 || player->powers[pw_infrared] & 8; +} diff --git a/src/p_user.h b/src/p_user.h new file mode 100644 index 0000000..a2b202c --- /dev/null +++ b/src/p_user.h @@ -0,0 +1,49 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Player related stuff. + * Bobbing POV/weapon, movement. + * Pending weapon. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_USER__ +#define __P_USER__ + +#include "d_player.h" + +void P_PlayerThink(player_t *player); +void P_CalcHeight(player_t *player); +void P_DeathThink(player_t *player); +void P_MovePlayer(player_t *player); +void P_Thrust(player_t *player, angle_t angle, fixed_t move); + +void P_SetPitch(player_t *player); + +#endif /* __P_USER__ */ diff --git a/src/protocol.h b/src/protocol.h new file mode 100644 index 0000000..68ddfb5 --- /dev/null +++ b/src/protocol.h @@ -0,0 +1,101 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Doom Network protocol packet definitions. + *-----------------------------------------------------------------------------*/ + +#ifndef __PROTOCOL__ +#define __PROTOCOL__ + +#include "doomtype.h" +#include "d_ticcmd.h" +#include "m_swap.h" + +enum packet_type_e { + PKT_INIT, // initial packet to server + PKT_SETUP, // game information packet + PKT_GO, // game has started + PKT_TICC, // tics from client + PKT_TICS, // tics from server + PKT_RETRANS, // Request for retransmission + PKT_EXTRA, // Extra info packet + PKT_QUIT, // Player quit game + PKT_DOWN, // Server downed + PKT_WAD, // Wad file request + PKT_BACKOFF, // Request for client back-off +}; + +typedef struct { + byte checksum; // Simple checksum of the entire packet + byte type; /* Type of packet */ + byte reserved[2]; /* Was random in prboom <=2.2.4, now 0 */ + unsigned tic; // Timestamp +} PACKEDATTR packet_header_t; + +static inline void packet_set(packet_header_t* p, enum packet_type_e t, unsigned long tic) +{ p->tic = doom_htonl(tic); p->type = t; p->reserved[0] = 0; p->reserved[1] = 0; } + +#ifndef GAME_OPTIONS_SIZE +// From g_game.h +#define GAME_OPTIONS_SIZE 64 +#endif + +struct setup_packet_s { + byte players, yourplayer, skill, episode, level, deathmatch, complevel, ticdup, extratic; + byte game_options[GAME_OPTIONS_SIZE]; + byte numwads; + byte wadnames[1]; // Actually longer +}; + +/* cph - convert network byte stream to usable ticcmd_t and visa-versa + * - the functions are functionally identical apart from parameters + * - the void* param can be unaligned. By using void* as the parameter + * it means gcc won't assume alignment so won't make false assumptions + * when optimising. So I'm told. + */ +inline static void RawToTic(ticcmd_t* dst, const void* src) +{ + memcpy(dst,src,sizeof *dst); + dst->angleturn = doom_ntohs(dst->angleturn); + dst->consistancy = doom_ntohs(dst->consistancy); +} + +inline static void TicToRaw(void* dst, const ticcmd_t* src) +{ + /* We have to make a copy of the source struct, then do byte swaps, + * and fnially copy to the destination (can't do the swaps in the + * destination, because it might not be aligned). + */ + ticcmd_t tmp = *src; + tmp.angleturn = doom_ntohs(tmp.angleturn); + tmp.consistancy = doom_ntohs(tmp.consistancy); + memcpy(dst,&tmp,sizeof tmp); +} + +#endif // __PROTOCOL__ diff --git a/src/r_bsp.c b/src/r_bsp.c new file mode 100644 index 0000000..5730402 --- /dev/null +++ b/src/r_bsp.c @@ -0,0 +1,816 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * BSP traversal, handling of LineSegs for rendering. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "m_bbox.h" +#include "r_main.h" +#include "r_segs.h" +#include "r_plane.h" +#include "r_things.h" +#include "r_bsp.h" // cph - sanity checking +#include "v_video.h" +#include "lprintf.h" + +int currentsubsectornum; + +seg_t *curline; +side_t *sidedef; +line_t *linedef; +sector_t *frontsector; +sector_t *backsector; +drawseg_t *ds_p; + +// killough 4/7/98: indicates doors closed wrt automap bugfix: +// cph - replaced by linedef rendering flags - int doorclosed; + +// killough: New code which removes 2s linedef limit +drawseg_t *drawsegs; +unsigned maxdrawsegs; +// drawseg_t drawsegs[MAXDRAWSEGS]; // old code -- killough + +// +// R_ClearDrawSegs +// + +void R_ClearDrawSegs(void) +{ + ds_p = drawsegs; +} + +// CPhipps - +// Instead of clipsegs, let's try using an array with one entry for each column, +// indicating whether it's blocked by a solid wall yet or not. + +// e6y: resolution limitation is removed +byte *solidcol; + +// CPhipps - +// R_ClipWallSegment +// +// Replaces the old R_Clip*WallSegment functions. It draws bits of walls in those +// columns which aren't solid, and updates the solidcol[] array appropriately + +static void R_ClipWallSegment(int first, int last, dboolean solid) +{ + byte *p; + while (first < last) { + if (solidcol[first]) { + if (!(p = memchr(solidcol+first, 0, last-first))) return; // All solid + first = p - solidcol; + } else { + int to; + if (!(p = memchr(solidcol+first, 1, last-first))) to = last; + else to = p - solidcol; + R_StoreWallRange(first, to-1); + if (solid) { + memset(solidcol+first,1,to-first); + } + first = to; + } + } +} + +// +// R_ClearClipSegs +// + +void R_ClearClipSegs (void) +{ + memset(solidcol, 0, SCREENWIDTH); +} + +// killough 1/18/98 -- This function is used to fix the automap bug which +// showed lines behind closed doors simply because the door had a dropoff. +// +// cph - converted to R_RecalcLineFlags. This recalculates all the flags for +// a line, including closure and texture tiling. + +static void R_RecalcLineFlags(line_t *linedef) +{ + linedef->r_validcount = gametic; + + /* First decide if the line is closed, normal, or invisible */ + if (!(linedef->flags & ML_TWOSIDED) + || backsector->ceilingheight <= frontsector->floorheight + || backsector->floorheight >= frontsector->ceilingheight + || ( + // if door is closed because back is shut: + backsector->ceilingheight <= backsector->floorheight + + // preserve a kind of transparent door/lift special effect: + && (backsector->ceilingheight >= frontsector->ceilingheight || + curline->sidedef->toptexture) + + && (backsector->floorheight <= frontsector->floorheight || + curline->sidedef->bottomtexture) + + // properly render skies (consider door "open" if both ceilings are sky): + && (backsector->ceilingpic !=skyflatnum || + frontsector->ceilingpic!=skyflatnum) + ) + ) + linedef->r_flags = RF_CLOSED; + else { + // 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. + // CPhipps - recode for speed, not certain if this is portable though + if (backsector->ceilingheight != frontsector->ceilingheight + || backsector->floorheight != frontsector->floorheight + || curline->sidedef->midtexture + || memcmp(&backsector->floor_xoffs, &frontsector->floor_xoffs, + sizeof(frontsector->floor_xoffs) + sizeof(frontsector->floor_yoffs) + + sizeof(frontsector->ceiling_xoffs) + sizeof(frontsector->ceiling_yoffs) + + sizeof(frontsector->ceilingpic) + sizeof(frontsector->floorpic) + + sizeof(frontsector->lightlevel) + sizeof(frontsector->floorlightsec) + + sizeof(frontsector->ceilinglightsec))) { + linedef->r_flags = 0; return; + } else + linedef->r_flags = RF_IGNORE; + } + + /* cph - I'm too lazy to try and work with offsets in this */ + if (curline->sidedef->rowoffset) return; + + /* Now decide on texture tiling */ + if (linedef->flags & ML_TWOSIDED) { + int c; + + /* Does top texture need tiling */ + if ((c = frontsector->ceilingheight - backsector->ceilingheight) > 0 && + (textureheight[texturetranslation[curline->sidedef->toptexture]] > c)) + linedef->r_flags |= RF_TOP_TILE; + + /* Does bottom texture need tiling */ + if ((c = frontsector->floorheight - backsector->floorheight) > 0 && + (textureheight[texturetranslation[curline->sidedef->bottomtexture]] > c)) + linedef->r_flags |= RF_BOT_TILE; + } else { + int c; + /* Does middle texture need tiling */ + if ((c = frontsector->ceilingheight - frontsector->floorheight) > 0 && + (textureheight[texturetranslation[curline->sidedef->midtexture]] > c)) + linedef->r_flags |= RF_MID_TILE; + } +} + +// +// killough 3/7/98: Hack floor/ceiling heights for deep water etc. +// +// If player's view height is underneath fake floor, lower the +// drawn ceiling to be just under the floor height, and replace +// the drawn floor and ceiling textures, and light level, with +// the control sector's. +// +// Similar for ceiling, only reflected. +// +// killough 4/11/98, 4/13/98: fix bugs, add 'back' parameter +// + +sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, + int *floorlightlevel, int *ceilinglightlevel, + dboolean back) +{ + if (floorlightlevel) + *floorlightlevel = sec->floorlightsec == -1 ? + sec->lightlevel : sectors[sec->floorlightsec].lightlevel; + + if (ceilinglightlevel) + *ceilinglightlevel = sec->ceilinglightsec == -1 ? // killough 4/11/98 + sec->lightlevel : sectors[sec->ceilinglightsec].lightlevel; + + if (sec->heightsec != -1) + { + const sector_t *s = §ors[sec->heightsec]; + int heightsec = viewplayer->mo->subsector->sector->heightsec; + int underwater = heightsec!=-1 && viewz<=sectors[heightsec].floorheight; + + // Replace sector being drawn, with a copy to be hacked + *tempsec = *sec; + + // Replace floor and ceiling height with other sector's heights. + tempsec->floorheight = s->floorheight; + tempsec->ceilingheight = s->ceilingheight; + + // killough 11/98: prevent sudden light changes from non-water sectors: + if (underwater && (tempsec-> floorheight = sec->floorheight, + tempsec->ceilingheight = s->floorheight-1, !back)) + { // head-below-floor hack + tempsec->floorpic = s->floorpic; + tempsec->floor_xoffs = s->floor_xoffs; + tempsec->floor_yoffs = s->floor_yoffs; + + if (underwater) { + if (s->ceilingpic == skyflatnum) { + tempsec->floorheight = tempsec->ceilingheight+1; + tempsec->ceilingpic = tempsec->floorpic; + tempsec->ceiling_xoffs = tempsec->floor_xoffs; + tempsec->ceiling_yoffs = tempsec->floor_yoffs; + } else { + tempsec->ceilingpic = s->ceilingpic; + tempsec->ceiling_xoffs = s->ceiling_xoffs; + tempsec->ceiling_yoffs = s->ceiling_yoffs; + } + } + + tempsec->lightlevel = s->lightlevel; + + if (floorlightlevel) + *floorlightlevel = s->floorlightsec == -1 ? s->lightlevel : + sectors[s->floorlightsec].lightlevel; // killough 3/16/98 + + if (ceilinglightlevel) + *ceilinglightlevel = s->ceilinglightsec == -1 ? s->lightlevel : + sectors[s->ceilinglightsec].lightlevel; // killough 4/11/98 + } + else + if (heightsec != -1 && viewz >= sectors[heightsec].ceilingheight && + sec->ceilingheight > s->ceilingheight) + { // Above-ceiling hack + tempsec->ceilingheight = s->ceilingheight; + tempsec->floorheight = s->ceilingheight + 1; + + tempsec->floorpic = tempsec->ceilingpic = s->ceilingpic; + tempsec->floor_xoffs = tempsec->ceiling_xoffs = s->ceiling_xoffs; + tempsec->floor_yoffs = tempsec->ceiling_yoffs = s->ceiling_yoffs; + + if (s->floorpic != skyflatnum) + { + tempsec->ceilingheight = sec->ceilingheight; + tempsec->floorpic = s->floorpic; + tempsec->floor_xoffs = s->floor_xoffs; + tempsec->floor_yoffs = s->floor_yoffs; + } + + tempsec->lightlevel = s->lightlevel; + + if (floorlightlevel) + *floorlightlevel = s->floorlightsec == -1 ? s->lightlevel : + sectors[s->floorlightsec].lightlevel; // killough 3/16/98 + + if (ceilinglightlevel) + *ceilinglightlevel = s->ceilinglightsec == -1 ? s->lightlevel : + sectors[s->ceilinglightsec].lightlevel; // killough 4/11/98 + } + sec = tempsec; // Use other sector + } + return sec; +} + +// +// e6y: Check whether the player can look beyond this line +// + +static dboolean CheckClip(seg_t * seg, sector_t * frontsector, sector_t * backsector) +{ + static sector_t tempsec_back, tempsec_front; + + backsector = R_FakeFlat(backsector, &tempsec_back, NULL, NULL, true); + frontsector = R_FakeFlat(frontsector, &tempsec_front, NULL, NULL, false); + + // check for closed sectors! + if (backsector->ceilingheight <= frontsector->floorheight) + { + if (seg->sidedef->toptexture == NO_TEXTURE) + return false; + + if (backsector->ceilingpic == skyflatnum && frontsector->ceilingpic == skyflatnum) + return false; + + return true; + } + + if (frontsector->ceilingheight <= backsector->floorheight) + { + if (seg->sidedef->bottomtexture == NO_TEXTURE) + return false; + + // properly render skies (consider door "open" if both floors are sky): + if (backsector->ceilingpic == skyflatnum && frontsector->ceilingpic == skyflatnum) + return false; + + return true; + } + + if (backsector->ceilingheight <= backsector->floorheight) + { + // preserve a kind of transparent door/lift special effect: + if (backsector->ceilingheight < frontsector->ceilingheight) + { + if (seg->sidedef->toptexture == NO_TEXTURE) + return false; + } + if (backsector->floorheight > frontsector->floorheight) + { + if (seg->sidedef->bottomtexture == NO_TEXTURE) + return false; + } + if (backsector->ceilingpic == skyflatnum && frontsector->ceilingpic == skyflatnum) + return false; + + if (backsector->floorpic == skyflatnum && frontsector->floorpic == skyflatnum) + return false; + + return true; + } + + return false; +} + +// +// R_AddLine +// Clips the given segment +// and adds any visible pieces to the line list. +// + +static void R_AddLine (seg_t *line) +{ + int x1; + int x2; + angle_t angle1; + angle_t angle2; + angle_t span; + angle_t tspan; + static sector_t tempsec; // killough 3/8/98: ceiling/water hack + + curline = line; + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + angle1 = R_PointToPseudoAngle(line->v1->x, line->v1->y); + angle2 = R_PointToPseudoAngle(line->v2->x, line->v2->y); + + // Back side, i.e. backface culling - read: endAngle >= startAngle! + if (angle2 - angle1 < ANG180 || !line->linedef) + { + return; + } + if (!gld_clipper_SafeCheckRange(angle2, angle1)) + { + return; + } + + map_subsectors[currentsubsectornum] = 1; + + if (!line->backsector) + { + gld_clipper_SafeAddClipRange(angle2, angle1); + } + else + { + if (line->frontsector == line->backsector) + { + if (texturetranslation[line->sidedef->midtexture] == NO_TEXTURE) + { + //e6y: nothing to do here! + return; + } + } + if (CheckClip(line, line->frontsector, line->backsector)) + { + gld_clipper_SafeAddClipRange(angle2, angle1); + } + } + + if (ds_p == drawsegs+maxdrawsegs) // killough 1/98 -- fix 2s line HOM + { + unsigned pos = ds_p - drawsegs; // jff 8/9/98 fix from ZDOOM1.14a + unsigned newmax = maxdrawsegs ? maxdrawsegs*2 : 128; // killough + drawsegs = realloc(drawsegs,newmax*sizeof(*drawsegs)); + ds_p = drawsegs + pos; // jff 8/9/98 fix from ZDOOM1.14a + maxdrawsegs = newmax; + } + + if(curline->miniseg == false) // figgi -- skip minisegs + curline->linedef->flags |= ML_MAPPED; + + // proff 11/99: the rest of the calculations is not needed for OpenGL + ds_p++->curline = curline; + gld_AddWall(curline); + + return; + } +#endif + + angle1 = R_PointToAngleEx(line->v1->px, line->v1->py); + angle2 = R_PointToAngleEx(line->v2->px, line->v2->py); + + // Clip to view edges. + 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 = 0-clipangle; + } + + // The seg is in the view range, + // but not necessarily visible. + + angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT; + angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT; + + // killough 1/31/98: Here is where "slime trails" can SOMETIMES occur: + x1 = viewangletox[angle1]; + x2 = viewangletox[angle2]; + + // Does not cross a pixel? + if (x1 >= x2) // killough 1/31/98 -- change == to >= for robustness + return; + + backsector = line->backsector; + + // Single sided line? + if (backsector) + // killough 3/8/98, 4/4/98: hack for invisible ceilings / deep water + backsector = R_FakeFlat(backsector, &tempsec, NULL, NULL, true); + + /* cph - roll up linedef properties in flags */ + if ((linedef = curline->linedef)->r_validcount != gametic) + R_RecalcLineFlags(linedef); + + if (linedef->r_flags & RF_IGNORE) + { + return; + } + else + R_ClipWallSegment (x1, x2, linedef->r_flags & RF_CLOSED); +} + +// +// R_CheckBBox +// Checks BSP node/subtree bounding box. +// Returns true +// if some part of the bbox might be visible. +// + +static const int checkcoord[12][4] = // killough -- static const +{ + {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} +}; + +// killough 1/28/98: static // CPhipps - const parameter, reformatted +static dboolean R_CheckBBox(const fixed_t *bspcoord) +{ + angle_t angle1, angle2; + int boxpos; + const int* check; + + // Find the corners of the box + // that define the edges from current viewpoint. + boxpos = (viewx <= bspcoord[BOXLEFT] ? 0 : viewx < bspcoord[BOXRIGHT ] ? 1 : 2) + + (viewy >= bspcoord[BOXTOP ] ? 0 : viewy > bspcoord[BOXBOTTOM] ? 4 : 8); + + if (boxpos == 5) + return true; + + check = checkcoord[boxpos]; + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + angle1 = R_PointToPseudoAngle(bspcoord[check[0]], bspcoord[check[1]]); + angle2 = R_PointToPseudoAngle(bspcoord[check[2]], bspcoord[check[3]]); + return gld_clipper_SafeCheckRange(angle2, angle1); + } +#endif + + angle1 = R_PointToAngleEx (bspcoord[check[0]], bspcoord[check[1]]) - viewangle; + angle2 = R_PointToAngleEx (bspcoord[check[2]], bspcoord[check[3]]) - viewangle; + + // cph - replaced old code, which was unclear and badly commented + // Much more efficient code now + if ((signed)angle1 < (signed)angle2) { /* it's "behind" us */ + /* Either angle1 or angle2 is behind us, so it doesn't matter if we + * change it to the corect sign + */ + if ((angle1 >= ANG180) && (angle1 < ANG270)) + angle1 = INT_MAX; /* which is ANG180-1 */ + else + angle2 = INT_MIN; + } + + if ((signed)angle2 >= (signed)clipangle) return false; // Both off left edge + if ((signed)angle1 <= -(signed)clipangle) return false; // Both off right edge + if ((signed)angle1 >= (signed)clipangle) angle1 = clipangle; // Clip at left edge + if ((signed)angle2 <= -(signed)clipangle) angle2 = 0-clipangle; // Clip at right edge + + // Find the first clippost + // that touches the source post + // (adjacent pixels are touching). + angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT; + angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT; + { + int sx1 = viewangletox[angle1]; + int sx2 = viewangletox[angle2]; + // const cliprange_t *start; + + // Does not cross a pixel. + if (sx1 == sx2) + return false; + + if (!memchr(solidcol+sx1, 0, sx2-sx1)) return false; + // All columns it covers are already solidly covered + } + + return true; +} + +// +// R_Subsector +// Determine floor/ceiling planes. +// Add sprites of things in sector. +// Draw one or more line segments. +// +// killough 1/31/98 -- made static, polished + +static void R_Subsector(int num) +{ + int count; + seg_t *line; + subsector_t *sub; + sector_t tempsec; // killough 3/7/98: deep water hack + int floorlightlevel; // killough 3/16/98: set floor lightlevel + int ceilinglightlevel; // killough 4/11/98 + #ifdef GL_DOOM + // dmooter 1/16/2017 Move from being declared next to its use several lines lower. + // Needs to remain in scope to the end of the function so its stack memory is recycled, + // compiler optimizations will make the floorplane pointer a dangling pointer + // when passed into gld_AddPlane(). + visplane_t dummyfloorplane; + visplane_t dummyceilingplane; + #endif + +#ifdef RANGECHECK + if (num>=numsubsectors) + I_Error ("R_Subsector: ss %i with numss = %i", num, numsubsectors); +#endif + + sub = &subsectors[num]; + currentsubsectornum = num; + +#ifdef GL_DOOM + if (V_GetMode() != VID_MODEGL || !gl_use_stencil || sub->sector->validcount != validcount) +#endif + { + frontsector = sub->sector; + + // killough 3/8/98, 4/4/98: Deep water / fake ceiling effect + frontsector = R_FakeFlat(frontsector, &tempsec, &floorlightlevel, + &ceilinglightlevel, false); // killough 4/11/98 + + // killough 3/7/98: Add (x,y) offsets to flats, add deep water check + // killough 3/16/98: add floorlightlevel + // killough 10/98: add support for skies transferred from sidedefs + floorplane = frontsector->floorheight < viewz || // killough 3/7/98 + (frontsector->heightsec != -1 && + sectors[frontsector->heightsec].ceilingpic == skyflatnum) ? + R_FindPlane(frontsector->floorheight, + frontsector->floorpic == skyflatnum && // kilough 10/98 + frontsector->sky & PL_SKYFLAT ? frontsector->sky : + frontsector->floorpic, + floorlightlevel, // killough 3/16/98 + frontsector->floor_xoffs, // killough 3/7/98 + frontsector->floor_yoffs + ) : NULL; + + ceilingplane = frontsector->ceilingheight > viewz || + frontsector->ceilingpic == skyflatnum || + (frontsector->heightsec != -1 && + sectors[frontsector->heightsec].floorpic == skyflatnum) ? + R_FindPlane(frontsector->ceilingheight, // killough 3/8/98 + frontsector->ceilingpic == skyflatnum && // kilough 10/98 + frontsector->sky & PL_SKYFLAT ? frontsector->sky : + frontsector->ceilingpic, + ceilinglightlevel, // killough 4/11/98 + frontsector->ceiling_xoffs, // killough 3/7/98 + frontsector->ceiling_yoffs + ) : NULL; + } + + // e6y + // New algo can handle fake flats and ceilings + // much more correctly and fastly the the original +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + // check if the sector is faked + sector_t *tmpsec = NULL; + + if(frontsector == sub->sector) + { + if (!gl_use_stencil) + { + + // if the sector has bottomtextures, then the floorheight will be set to the + // highest surounding floorheight + if ((frontsector->flags & NO_BOTTOMTEXTURES) || (!floorplane)) + { + tmpsec = GetBestFake(frontsector, 0, validcount); + + if (tmpsec && frontsector->floorheight != tmpsec->floorheight) + { + dummyfloorplane.height = tmpsec->floorheight; + dummyfloorplane.lightlevel = tmpsec->lightlevel; + dummyfloorplane.picnum = tmpsec->floorpic; + floorplane = &dummyfloorplane; + } + } + + // the same for ceilings. they will be set to the lowest ceilingheight + if ((frontsector->flags & NO_TOPTEXTURES) || (!ceilingplane)) + { + tmpsec = GetBestFake(frontsector, 1, validcount); + + if (tmpsec && frontsector->ceilingheight != tmpsec->ceilingheight) + { + dummyceilingplane.height = tmpsec->ceilingheight; + dummyceilingplane.lightlevel = tmpsec->lightlevel; + dummyceilingplane.picnum = tmpsec->ceilingpic; + ceilingplane = &dummyceilingplane; + } + } + } + + /* + * Floors higher than the player's viewheight, or ceilings lower + * than the player's viewheight with no textures will bleed the + * sector behind them through in the software renderer. This is + * occasionally used to create an "invisible wall" effect to hide + * monsters, but in the GL renderer would leave an untextured space + * beneath or above unless otherwise patched. + * + * This code attempts to find an appropriate sector to "bleed + * through" over the untextured gap. + * + * Note there is a corner case that is not handled: If a dummy + * sector off-screen is the lowest adjacent sector to the invisible + * wall, and it is at a different height than the correct + * bleed-through sector, the dummy sector is copied instead of the + * sector behind the player. It may be possible to address this in + * a future patch by refactoring this into the renderer and tagging + * visible candidate sectors during drawing. + */ + if (frontsector->floorheight >= viewz && (frontsector->flags & MISSING_BOTTOMTEXTURES)) + { + tmpsec = GetBestBleedSector(frontsector, 0); + + if (tmpsec) + { + dummyfloorplane.height = tmpsec->floorheight; + dummyfloorplane.lightlevel = tmpsec->lightlevel; + dummyfloorplane.picnum = tmpsec->floorpic; + floorplane = &dummyfloorplane; + } + } + + if (frontsector->ceilingheight <= viewz && (frontsector->flags & MISSING_TOPTEXTURES)) + { + tmpsec = GetBestBleedSector(frontsector, 1); + + if (tmpsec) + { + dummyceilingplane.height = tmpsec->ceilingheight; + dummyceilingplane.lightlevel = tmpsec->lightlevel; + dummyceilingplane.picnum = tmpsec->ceilingpic; + ceilingplane = &dummyceilingplane; + } + } + } + } +#endif + + // killough 9/18/98: Fix underwater slowdown, by passing real sector + // instead of fake one. Improve sprite lighting by basing sprite + // lightlevels on floor & ceiling lightlevels in the surrounding area. + // + // 10/98 killough: + // + // NOTE: TeamTNT fixed this bug incorrectly, messing up sprite lighting!!! + // That is part of the 242 effect!!! If you simply pass sub->sector to + // the old code you will not get correct lighting for underwater sprites!!! + // Either you must pass the fake sector and handle validcount here, on the + // real sector, or you must account for the lighting in some other way, + // like passing it as an argument. + + if (sub->sector->validcount != validcount) + { + sub->sector->validcount = validcount; + + R_AddSprites(sub, (floorlightlevel+ceilinglightlevel)/2); + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + gld_AddPlane(num, floorplane, ceilingplane); +#endif + } + + count = sub->numlines; + line = &segs[sub->firstline]; + while (count--) + { + if (line->miniseg == false) + R_AddLine (line); + line++; + curline = NULL; /* cph 2001/11/18 - must clear curline now we're done with it, so R_ColourMap doesn't try using it for other things */ + } +} + +// +// RenderBSPNode +// Renders all subsectors below a given node, +// traversing subtree recursively. +// Just call with BSP root. +// +// killough 5/2/98: reformatted, removed tail recursion + +void R_RenderBSPNode(int bspnum) +{ + while (!(bspnum & NF_SUBSECTOR)) // Found a subsector? + { + const node_t *bsp = &nodes[bspnum]; + + // Decide which side the view point is on. + int side = R_PointOnSide(viewx, viewy, bsp); + // Recursively divide front space. + R_RenderBSPNode(bsp->children[side]); + + // Possibly divide back space. + + if (!R_CheckBBox(bsp->bbox[side^1])) + return; + + bspnum = bsp->children[side^1]; + } + // e6y: support for extended nodes + R_Subsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR); +} diff --git a/src/r_bsp.h b/src/r_bsp.h new file mode 100644 index 0000000..59fec86 --- /dev/null +++ b/src/r_bsp.h @@ -0,0 +1,65 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh module, BSP traversal and handling. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_BSP__ +#define __R_BSP__ + +#ifdef __GNUG__ +#pragma interface +#endif + +extern seg_t *curline; +extern side_t *sidedef; +extern line_t *linedef; +extern sector_t *frontsector; +extern sector_t *backsector; + +/* old code -- killough: + * extern drawseg_t drawsegs[MAXDRAWSEGS]; + * new code -- killough: */ +extern drawseg_t *drawsegs; +extern unsigned maxdrawsegs; + +// e6y: resolution limitation is removed +extern byte *solidcol; + +extern drawseg_t *ds_p; + +void R_ClearClipSegs(void); +void R_ClearDrawSegs(void); +void R_RenderBSPNode(int bspnum); + +/* killough 4/13/98: fake floors/ceilings for deep water / fake ceilings: */ +sector_t *R_FakeFlat(sector_t *, sector_t *, int *, int *, dboolean); + +#endif diff --git a/src/r_data.c b/src/r_data.c new file mode 100644 index 0000000..6138a46 --- /dev/null +++ b/src/r_data.c @@ -0,0 +1,792 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Preparation of data for rendering, + * generation of lookups, caching, retrieval by name. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "w_wad.h" +#include "r_draw.h" +#include "r_main.h" +#include "r_sky.h" +#include "i_system.h" +#include "r_bsp.h" +#include "r_things.h" +#include "p_tick.h" +#include "lprintf.h" // jff 08/03/98 - declaration of lprintf +#include "p_tick.h" + +#include "m_io.h" + +// +// Graphics. +// DOOM graphics for walls and sprites +// is stored in vertical runs of opaque pixels (posts). +// A column is composed of zero or more posts, +// a patch or sprite is composed of zero or more columns. +// + +// +// Texture definition. +// Each texture is composed of one or more patches, +// with patches being lumps stored in the WAD. +// The lumps are referenced by number, and patched +// into the rectangular texture space using origin +// and possibly other attributes. +// + +typedef struct +{ + short originx; + short originy; + short patch; + short stepdir; // unused in Doom but might be used in Phase 2 Boom + short colormap; // unused in Doom but might be used in Phase 2 Boom +} PACKEDATTR mappatch_t; + + +typedef struct +{ + char name[8]; + char pad2[4]; // unused + short width; + short height; + char pad[4]; // unused in Doom but might be used in Boom Phase 2 + short patchcount; + mappatch_t patches[1]; +} PACKEDATTR maptexture_t; + +// A maptexturedef_t describes a rectangular texture, which is composed +// of one or more mappatch_t structures that arrange graphic patches. + +// killough 4/17/98: make firstcolormaplump,lastcolormaplump external +int firstcolormaplump, lastcolormaplump; // killough 4/17/98 + +int firstflat, lastflat, numflats; +int firstspritelump, lastspritelump, numspritelumps; +int numtextures; +texture_t **textures; // proff - 04/05/2000 removed static for OpenGL +fixed_t *textureheight; //needed for texture pegging (and TFE fix - killough) +int *flattranslation; // for global animation +int *texturetranslation; + +// +// R_GetTextureColumn +// + +const byte *R_GetTextureColumn(const rpatch_t *texpatch, int col) { + while (col < 0) + col += texpatch->width; + col &= texpatch->widthmask; + + return texpatch->columns[col].pixels; +} + +// +// R_InitTextures +// Initializes the texture list +// with the textures from the world map. +// + +static void R_InitTextures (void) +{ + const maptexture_t *mtexture; + texture_t *texture; + const mappatch_t *mpatch; + texpatch_t *patch; + int i, j; + int maptex_lump[2] = {-1, -1}; + const int *maptex; + const int *maptex1, *maptex2; + char name[9]; + int names_lump; // cph - new wad lump handling + const char *names; // cph - + const char *name_p;// const*'s + int *patchlookup; + int nummappatches; + int offset; + int maxoff, maxoff2; + int numtextures1, numtextures2; + const int *directory; + int errors = 0; + + // Load the patch names from pnames.lmp. + name[8] = 0; + names = W_CacheLumpNum(names_lump = W_GetNumForName("PNAMES")); + nummappatches = LittleLong(*((const int *)names)); + name_p = names+4; + patchlookup = malloc(nummappatches*sizeof(*patchlookup)); // killough + + for (i=0 ; i maxoff) + I_Error("R_InitTextures: Bad texture directory"); + + mtexture = (const maptexture_t *) ( (const byte *)maptex + offset); + + texture = textures[i] = + Z_Malloc(sizeof(texture_t) + + sizeof(texpatch_t)*(LittleShort(mtexture->patchcount)-1), + PU_STATIC, 0); + + texture->width = LittleShort(mtexture->width); + texture->height = LittleShort(mtexture->height); + texture->patchcount = LittleShort(mtexture->patchcount); + + /* Mattias Engdegrd emailed me of the following explenation of + * why memcpy doesnt work on some systems: + * "I suppose it is the mad unaligned allocation + * going on (and which gcc in some way manages to cope with + * through the __attribute__ ((packed))), and which it forgets + * when optimizing memcpy (to a single word move) since it appears + * to be aligned. Technically a gcc bug, but I can't blame it when + * it's stressed with that amount of + * non-standard nonsense." + * So in short the unaligned struct confuses gcc's optimizer so + * i took the memcpy out alltogether to avoid future problems-Jess + */ + /* The above was #ifndef SPARC, but i got a mail from + * Putera Joseph F NPRI containing: + * I had to use the memcpy function on a sparc machine. The + * other one would give me a core dump. + * cph - I find it hard to believe that sparc memcpy is broken, + * but I don't believe the pointers to memcpy have to be aligned + * either. Use fast memcpy on other machines anyway. + */ +/* + proff - I took this out, because Oli Kraus (olikraus@yahoo.com) told + me the memcpy produced a buserror. Since this function isn't time- + critical I'm using the for loop now. +*/ +/* +#ifndef GCC + memcpy(texture->name, mtexture->name, sizeof(texture->name)); +#else +*/ + { + size_t j; + for(j=0;jname);j++) + texture->name[j]=mtexture->name[j]; + } +/* #endif */ + + mpatch = mtexture->patches; + patch = texture->patches; + + for (j=0 ; jpatchcount ; j++, mpatch++, patch++) + { + patch->originx = LittleShort(mpatch->originx); + patch->originy = LittleShort(mpatch->originy); + patch->patch = patchlookup[LittleShort(mpatch->patch)]; + if (patch->patch == -1) + { + //jff 8/3/98 use logical output routine + lprintf(LO_ERROR,"\nR_InitTextures: Missing patch %d in texture %.8s", + LittleShort(mpatch->patch), texture->name); // killough 4/17/98 + ++errors; + } + } + + for (j=1; j*2 <= texture->width; j<<=1) + ; + texture->widthmask = j-1; + textureheight[i] = texture->height<wadfile->name, + (doomverstr ? doomverstr : "DOOM")); + I_Error("R_InitTextures: %d errors", errors); + } + + // Precalculate whatever possible. + if (devparm) // cph - If in development mode, generate now so all errors are found at once + { + R_InitPatches(); //e6y + for (i=0 ; iindex = -1; + while (--i >= 0) + { + int j = W_LumpNameHash(textures[i]->name) % (unsigned) numtextures; + textures[i]->next = textures[j]->index; // Prepend to chain + textures[j]->index = i; + } +} + +// +// R_InitFlats +// +static void R_InitFlats(void) +{ + int i; + + firstflat = W_GetNumForName("F_START") + 1; + lastflat = W_GetNumForName("F_END") - 1; + numflats = lastflat - firstflat + 1; + + // Create translation table for global animation. + // killough 4/9/98: make column offsets 32-bit; + // clean up malloc-ing to use sizeof + + flattranslation = + Z_Malloc((numflats+1)*sizeof(*flattranslation), PU_STATIC, 0); + + for (i=0 ; i=0); + } + + // Next, compute all entries using minimum arithmetic. + + { + int i,j; + byte *tp = my_tranmap; + for (i=0;i<256;i++) + { + long r1 = pal[0][i] * w2; + long g1 = pal[1][i] * w2; + long b1 = pal[2][i] * w2; + if (!(i & 31) && progress) + //jff 8/3/98 use logical output routine + lprintf(LO_INFO,"."); + for (j=0;j<256;j++,tp++) + { + register int color = 255; + register long err; + long r = pal_w1[0][j] + r1; + long g = pal_w1[1][j] + g1; + long b = pal_w1[2][j] + b1; + long best = LONG_MAX; + do + if ((err = tot[color] - pal[0][color]*r + - pal[1][color]*g - pal[2][color]*b) < best) + best = err, *tp = color; + while (--color >= 0); + } + } + } + if ((cachefp = M_fopen(fname,"wb")) != NULL) // write out the cached translucency map + { + cache.pct = tran_filter_pct; + memcpy(cache.playpal, playpal, sizeof cache.playpal); + fseek(cachefp, 0, SEEK_SET); + fwrite(&cache, 1, sizeof cache, cachefp); + fwrite(main_tranmap, 256, 256, cachefp); + // CPhipps - leave close for a few lines... + } + } + + if (cachefp) // killough 11/98: fix filehandle leak + fclose(cachefp); + + free(fname); + + W_UnlockLumpName("PLAYPAL"); + } +} + +// +// R_InitData +// Locates all the lumps +// that will be used by all views +// Must be called after W_Init. +// + +void R_InitData(void) +{ + lprintf(LO_INFO, "Textures "); + R_InitTextures(); + lprintf(LO_INFO, "Flats "); + R_InitFlats(); + lprintf(LO_INFO, "Sprites "); + R_InitSpriteLumps(); + if (default_translucency) // killough 3/1/98 + R_InitTranMap(1); // killough 2/21/98, 3/6/98 + R_InitColormaps(); // killough 3/20/98 +} + +// +// R_FlatNumForName +// Retrieval, get a flat number for a flat name. +// +// killough 4/17/98: changed to use ns_flats namespace +// + +int R_FlatNumForName(const char *name) // killough -- const added +{ + int i = (W_CheckNumForName)(name, ns_flats); + if (i == -1) + { + // e6y + // Ability to play wads with wrong flat names + // Unknown flats will be replaced with "NO TEXTURE" preset from prboom-plus.wad + lprintf(LO_DEBUG, "R_FlatNumForName: %.8s not found\n", name); + i = (W_CheckNumForName)("-N0_TEX-", ns_flats); + if (i == -1) + { + I_Error("R_FlatNumForName: -N0_TEX- not found"); + } + } + return i - firstflat; +} + +// +// R_CheckTextureNumForName +// Check whether texture is available. +// Filter out NoTexture indicator. +// +// Rewritten by Lee Killough to use hash table for fast lookup. Considerably +// reduces the time needed to start new levels. See w_wad.c for comments on +// the hashing algorithm, which is also used for lump searches. +// +// killough 1/21/98, 1/31/98 +// + +int PUREFUNC R_CheckTextureNumForName(const char *name) +{ + int i = NO_TEXTURE; + if (*name != '-') // "NoTexture" marker. + { + i = textures[W_LumpNameHash(name) % (unsigned) numtextures]->index; + while (i >= 0 && strncasecmp(textures[i]->name,name,8)) + i = textures[i]->next; + } + return i; +} + +// +// R_TextureNumForName +// Calls R_CheckTextureNumForName, +// aborts with error message. +// + +int PUREFUNC R_TextureNumForName(const char *name) // const added -- killough +{ + int i = R_CheckTextureNumForName(name); + if (i == -1) + { + int lump = W_GetNumForName("TEXTURE1"); + const lumpinfo_t* info = W_GetLumpInfoByNum(lump); + lprintf(LO_INFO, "R_TextureNumForName: The file %s seems to be incompatible with \"%s\".\n", + info->wadfile->name, + (doomverstr ? doomverstr : "DOOM")); + I_Error("R_TextureNumForName: %.8s not found", name); + } + return i; +} + +// +// R_SafeTextureNumForName +// Calls R_CheckTextureNumForName, and changes any error to NO_TEXTURE +int PUREFUNC R_SafeTextureNumForName(const char *name, int snum) +{ + int i = R_CheckTextureNumForName(name); + if (i == -1) { + i = NO_TEXTURE; // e6y - return "no texture" + lprintf(LO_DEBUG,"bad texture '%s' in sidedef %d\n",name,snum); + } + return i; +} + +// +// R_PrecacheLevel +// Preloads all relevant graphics for the level. +// +// Totally rewritten by Lee Killough to use less memory, +// to avoid using alloca(), and to improve performance. +// cph - new wad lump handling, calls cache functions but acquires no locks + +static inline void precache_lump(int l) +{ + W_CacheLumpNum(l); W_UnlockLumpNum(l); +} + +void R_PrecacheLevel(void) +{ + register int i; + register byte *hitlist; + + if (timingdemo) + return; + + { + int size = numflats > numsprites ? numflats : numsprites; + hitlist = malloc(numtextures > size ? numtextures : size); + } + + // Precache flats. + + memset(hitlist, 0, numflats); + + for (i = numsectors; --i >= 0; ) + hitlist[sectors[i].floorpic] = hitlist[sectors[i].ceilingpic] = 1; + + for (i = numflats; --i >= 0; ) + if (hitlist[i]) + precache_lump(firstflat + i); + + // Precache textures. + + memset(hitlist, 0, numtextures); + + for (i = numsides; --i >= 0;) + hitlist[sides[i].bottomtexture] = + hitlist[sides[i].toptexture] = + hitlist[sides[i].midtexture] = 1; + + // Sky texture is always present. + // Note that F_SKY1 is the name used to + // indicate a sky floor/ceiling as a flat, + // while the sky texture is stored like + // a wall texture, with an episode dependend + // name. + + hitlist[skytexture] = 1; + + for (i = numtextures; --i >= 0; ) + if (hitlist[i]) + { + texture_t *texture = textures[i]; + int j = texture->patchcount; + while (--j >= 0) + precache_lump(texture->patches[j].patch); + } + + // Precache sprites. + memset(hitlist, 0, numsprites); + + { + thinker_t *th = NULL; + while ((th = P_NextThinker(th,th_all)) != NULL) + if (th->function == P_MobjThinker) + hitlist[((mobj_t *)th)->sprite] = 1; + } + + for (i=numsprites; --i >= 0;) + if (hitlist[i]) + { + int j = sprites[i].numframes; + while (--j >= 0) + { + short *sflump = sprites[i].spriteframes[j].lump; + int k = 7; + do + precache_lump(firstspritelump + sflump[k]); + while (--k >= 0); + } + } + free(hitlist); +} + +// Proff - Added for OpenGL +void R_SetPatchNum(patchnum_t *patchnum, const char *name) +{ + const rpatch_t *patch = R_CachePatchName(name); + patchnum->width = patch->width; + patchnum->height = patch->height; + patchnum->leftoffset = patch->leftoffset; + patchnum->topoffset = patch->topoffset; + patchnum->lumpnum = W_GetNumForName(name); + R_UnlockPatchName(name); +} + +void R_SetSpriteByNum(patchnum_t *patchnum, int lump) +{ + const rpatch_t *patch = R_CachePatchNum(lump); + patchnum->width = patch->width; + patchnum->height = patch->height; + patchnum->leftoffset = patch->leftoffset; + patchnum->topoffset = patch->topoffset; + patchnum->lumpnum = lump; + R_UnlockPatchNum(lump); +} + +int R_SetSpriteByIndex(patchnum_t *patchnum, spritenum_t item) +{ + int result = false; + if (item < NUMSPRITES) + { + int lump = firstspritelump + sprites[item].spriteframes->lump[0]; + R_SetSpriteByNum(patchnum, lump); + result = true; + } + return result; +} + +int R_SetSpriteByName(patchnum_t *patchnum, const char *name) +{ + int result = false; + patchnum->lumpnum = (W_CheckNumForName)(name, ns_sprites); + if (patchnum->lumpnum != -1) + { + R_SetSpriteByNum(patchnum, patchnum->lumpnum); + result = true; + } + return result; +} + +int R_SetPatchByName(patchnum_t *patchnum, const char *name) +{ + int result = false; + int lump = W_CheckNumForName(name); + if (lump != -1) + { + R_SetPatchNum(patchnum, name); + result = true; + } + return result; +} + +// e6y: Added for "GRNROCK" mostly +void R_SetFloorNum(patchnum_t *patchnum, const char *name) +{ + patchnum->width = 64; + patchnum->height = 64; + patchnum->leftoffset = 0; + patchnum->topoffset = 0; + patchnum->lumpnum = R_FlatNumForName(name); +} diff --git a/src/r_data.h b/src/r_data.h new file mode 100644 index 0000000..a3786ee --- /dev/null +++ b/src/r_data.h @@ -0,0 +1,112 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh module, data I/O, caching, retrieval of graphics + * by name. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __R_DATA__ +#define __R_DATA__ + +#include "r_defs.h" +#include "r_state.h" +#include "r_patch.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +// A single patch from a texture definition, basically +// a rectangular area within the texture rectangle. +typedef struct +{ + int originx, originy; // Block origin, which has already accounted + int patch; // for the internal origin of the patch. +} texpatch_t; + +// +// Texture definition. +// A DOOM wall texture is a list of patches +// which are to be combined in a predefined order. +// + +typedef struct +{ + char name[8]; // Keep name for switch changing, etc. + int next, index; // killough 1/31/98: used in hashing algorithm + // CPhipps - moved arrays with per-texture entries to elements here + unsigned widthmask; + // CPhipps - end of additions + short width, height; + short patchcount; // All the patches[patchcount] are drawn + texpatch_t patches[1]; // back-to-front into the cached texture. +} texture_t; + +extern int numtextures; +extern texture_t **textures; + + +const byte *R_GetTextureColumn(const rpatch_t *texpatch, int col); + + +// I/O, setting up the stuff. +void R_InitData (void); +void R_PrecacheLevel (void); + + +// Retrieval. +// Floor/ceiling opaque texture tiles, +// lookup by name. For animation? +int R_FlatNumForName (const char* name); // killough -- const added + + +// R_*TextureNumForName returns the texture number for the texture name, or NO_TEXTURE if +// there is no texture (i.e. "-") specified. +/* cph 2006/07/23 - defined value for no-texture marker (texture "-" in the WAD file) */ +#define NO_TEXTURE 0 +int PUREFUNC R_TextureNumForName (const char *name); // killough -- const added; cph - now PUREFUNC +int PUREFUNC R_SafeTextureNumForName (const char *name, int snum); +int PUREFUNC R_CheckTextureNumForName (const char *name); + +void R_InitTranMap(int); // killough 3/6/98: translucency initialization +int R_ColormapNumForName(const char *name); // killough 4/4/98 + +extern const byte *main_tranmap, *tranmap; + +/* Proff - Added for OpenGL - cph - const char* param */ +void R_SetPatchNum(patchnum_t *patchnum, const char *name); +// e6y: Added for "GRNROCK" mostly +void R_SetFloorNum(patchnum_t *patchnum, const char *name); +int R_SetSpriteByIndex(patchnum_t *patchnum, spritenum_t item); +int R_SetSpriteByName(patchnum_t *patchnum, const char *name); +int R_SetPatchByName(patchnum_t *patchnum, const char *name); + +#endif diff --git a/src/r_defs.h b/src/r_defs.h new file mode 100644 index 0000000..109286d --- /dev/null +++ b/src/r_defs.h @@ -0,0 +1,473 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh/rendering module, shared data struct definitions. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_DEFS__ +#define __R_DEFS__ + +// Screenwidth. +#include "doomdef.h" + +// Some more or less basic data types +// we depend on. +#include "m_fixed.h" + +// We rely on the thinker data struct +// to handle sound origins in sectors. +#include "d_think.h" + +// SECTORS do store MObjs anyway. +#include "p_mobj.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +// Silhouette, needed for clipping Segs (mainly) +// and sprites representing things. +#define SIL_NONE 0 +#define SIL_BOTTOM 1 +#define SIL_TOP 2 +#define SIL_BOTH 3 + +#define MAXDRAWSEGS 256 + +// +// INTERNAL MAP TYPES +// used by play and refresh +// + +// +// Your plain vanilla vertex. +// Note: transformed values not buffered locally, +// like some DOOM-alikes ("wt", "WebView") do. +// +typedef struct +{ + fixed_t x, y; + // [crispy] remove slime trails + // pseudovertexes are dummies that have their coordinates modified to get + // moved towards the linedef associated with their seg by projecting them + // using the law of cosines in p_setup.c:P_RemoveSlimeTrails(); + // they are *only* used in rendering + fixed_t px; + fixed_t py; +} vertex_t; + +// Each sector has a degenmobj_t in its center for sound origin purposes. +typedef struct +{ + thinker_t thinker; // not used for anything + fixed_t x, y, z; +} degenmobj_t; + +// +// The SECTORS record, at runtime. +// Stores things/mobjs. +// + +#define NO_TOPTEXTURES 0x00000001 +#define NO_BOTTOMTEXTURES 0x00000002 +#define SECTOR_IS_CLOSED 0x00000004 +#define NULL_SECTOR 0x00000008 +#define MISSING_TOPTEXTURES 0x00000010 +#define MISSING_BOTTOMTEXTURES 0x00000020 + +typedef struct +{ + int iSectorID; // proff 04/05/2000: needed for OpenGL and used in debugmode by the HUD to draw sectornum + unsigned int flags; //e6y: instead of .no_toptextures and .no_bottomtextures + fixed_t floorheight; + fixed_t ceilingheight; + int nexttag,firsttag; // killough 1/30/98: improves searches for tags. + int soundtraversed; // 0 = untraversed, 1,2 = sndlines-1 + mobj_t *soundtarget; // thing that made a sound (or null) + int blockbox[4]; // mapblock bounding box for height changes + int bbox[4]; // bounding box in map units + degenmobj_t soundorg; // origin for any sounds played by the sector + int validcount; // if == validcount, already checked + mobj_t *thinglist; // list of mobjs in sector + + /* killough 8/28/98: friction is a sector property, not an mobj property. + * these fields used to be in mobj_t, but presented performance problems + * when processed as mobj properties. Fix is to make them sector properties. + */ + int friction,movefactor; + + // thinker_t for reversable actions + void *floordata; // jff 2/22/98 make thinkers on + void *ceilingdata; // floors, ceilings, lighting, + void *lightingdata; // independent of one another + + // jff 2/26/98 lockout machinery for stairbuilding + int stairlock; // -2 on first locked -1 after thinker done 0 normally + int prevsec; // -1 or number of sector for previous step + int nextsec; // -1 or number of next step sector + + // killough 3/7/98: support flat heights drawn at another sector's heights + int heightsec; // other sector, or -1 if no other sector + + int bottommap, midmap, topmap; // killough 4/4/98: dynamic colormaps + + // list of mobjs that are at least partially in the sector + // thinglist is a subset of touching_thinglist + struct msecnode_s *touching_thinglist; // phares 3/14/98 + + int linecount; + struct line_s **lines; + + // killough 10/98: support skies coming from sidedefs. Allows scrolling + // skies and other effects. No "level info" kind of lump is needed, + // because you can use an arbitrary number of skies per level with this + // method. This field only applies when skyflatnum is used for floorpic + // or ceilingpic, because the rest of Doom needs to know which is sky + // and which isn't, etc. + + int sky; + + // killough 3/7/98: floor and ceiling texture offsets + fixed_t floor_xoffs, floor_yoffs; + fixed_t ceiling_xoffs, ceiling_yoffs; + + // killough 4/11/98: support for lightlevels coming from another sector + int floorlightsec, ceilinglightsec; + + short floorpic; + short ceilingpic; + short lightlevel; + short special; + short oldspecial; //jff 2/16/98 remembers if sector WAS secret (automap) + short tag; + + // [kb] For R_FixWiggle + int cachedheight; + int scaleindex; + + //e6y + int INTERP_SectorFloor; + int INTERP_SectorCeiling; + int INTERP_FloorPanning; + int INTERP_CeilingPanning; +#ifdef GL_DOOM + int fakegroup[2]; +#endif +} sector_t; + +// +// The SideDef. +// + +typedef struct +{ + fixed_t textureoffset; // add this to the calculated texture column + fixed_t rowoffset; // add this to the calculated texture top + short toptexture; // Texture indices. We do not maintain names here. + short bottomtexture; + short midtexture; + sector_t* sector; // Sector the SideDef is facing. + + // killough 4/4/98, 4/11/98: highest referencing special linedef's type, + // or lump number of special effect. Allows texture names to be overloaded + // for other functions. + + int special; + + int INTERP_WallPanning; +#ifdef GL_DOOM + int skybox_index; +#endif +} side_t; + +// +// Move clipping aid for LineDefs. +// +typedef enum +{ + ST_HORIZONTAL, + ST_VERTICAL, + ST_POSITIVE, + ST_NEGATIVE +} slopetype_t; + +typedef struct line_s +{ + int iLineID; // proff 04/05/2000: needed for OpenGL + vertex_t *v1, *v2; // Vertices, from v1 to v2. + fixed_t dx, dy; // Precalculated v2 - v1 for side checking. +#ifdef GL_DOOM + float texel_length; +#endif + unsigned short flags; // Animation related. + short special; + short tag; + unsigned short sidenum[2]; // Visual appearance: SideDefs. + fixed_t bbox[4]; // A bounding box, for the linedef's extent + slopetype_t slopetype; // To aid move clipping. + sector_t *frontsector; // Front and back sector. + sector_t *backsector; + int validcount; // if == validcount, already checked + void *specialdata; // thinker_t for reversable actions + int tranlump; // killough 4/11/98: translucency filter, -1 == none + int firsttag,nexttag; // killough 4/17/98: improves searches for tags. + int r_validcount; // cph: if == gametic, r_flags already done + enum { // cph: + RF_TOP_TILE = 1, // Upper texture needs tiling + RF_MID_TILE = 2, // Mid texture needs tiling + RF_BOT_TILE = 4, // Lower texture needs tiling + RF_IGNORE = 8, // Renderer can skip this line + RF_CLOSED =16, // Line blocks view + RF_ISOLATED =32, // Isolated line + } r_flags; + degenmobj_t soundorg; // sound origin for switches/buttons +} line_t; + +// phares 3/14/98 +// +// Sector list node showing all sectors an object appears in. +// +// There are two threads that flow through these nodes. The first thread +// starts at touching_thinglist in a sector_t and flows through the m_snext +// links to find all mobjs that are entirely or partially in the sector. +// The second thread starts at touching_sectorlist in an mobj_t and flows +// through the m_tnext links to find all sectors a thing touches. This is +// useful when applying friction or push effects to sectors. These effects +// can be done as thinkers that act upon all objects touching their sectors. +// As an mobj moves through the world, these nodes are created and +// destroyed, with the links changed appropriately. +// +// For the links, NULL means top or end of list. + +typedef struct msecnode_s +{ + sector_t *m_sector; // a sector containing this object + struct mobj_s *m_thing; // this object + struct msecnode_s *m_tprev; // prev msecnode_t for this thing + struct msecnode_s *m_tnext; // next msecnode_t for this thing + struct msecnode_s *m_sprev; // prev msecnode_t for this sector + struct msecnode_s *m_snext; // next msecnode_t for this sector + dboolean visited; // killough 4/4/98, 4/7/98: used in search algorithms +} msecnode_t; + +// +// The LineSeg. +// +typedef struct +{ + vertex_t *v1, *v2; + fixed_t offset; + angle_t angle; + angle_t pangle; // re-calculated angle used for rendering + int_64_t length; // fix long wall wobble + side_t* sidedef; + line_t* linedef; + + // figgi -- needed for glnodes + dboolean miniseg; + + + // Sector references. + // Could be retrieved from linedef, too + // (but that would be slower -- killough) + // backsector is NULL for one sided lines + + sector_t *frontsector, *backsector; +} seg_t; + +typedef struct ssline_s +{ + seg_t *seg; + line_t *linedef; + fixed_t x1, y1; + fixed_t x2, y2; + fixed_t bbox[4]; +} ssline_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; + // e6y: support for extended nodes + // 'int' instead of 'short' + int numlines, firstline; +} subsector_t; + + +// +// BSP node. +// +typedef struct +{ + fixed_t x, y, dx, dy; // Partition line. + fixed_t bbox[2][4]; // Bounding box for each child. + //unsigned short children[2]; // If NF_SUBSECTOR its a subsector. + int children[2]; // If NF_SUBSECTOR its a subsector. +} node_t; + +// +// OTHER TYPES +// + +// This could be wider for >8 bit display. +// Indeed, true color support is posibble +// precalculating 24bpp lightmap/colormap LUT. +// from darkening PLAYPAL to all black. +// Could use even more than 32 levels. + +typedef byte lighttable_t; + +// +// Masked 2s linedefs +// + +typedef struct drawseg_s +{ + seg_t *curline; + short x1, x2; + fixed_t scale1, scale2, scalestep; + int silhouette; // 0=none, 1=bottom, 2=top, 3=both + fixed_t bsilheight; // do not clip sprites above this + fixed_t tsilheight; // do not clip sprites below this + + // Added for filtering (fractional texture u coord) support - POPE + fixed_t rw_offset, rw_distance, rw_centerangle; + + // Pointers to lists for sprite clipping, + // all three adjusted so [x1] is first value. + + int *sprtopclip, *sprbottomclip, *maskedtexturecol; // dropoff overflow +} drawseg_t; + +// proff: Added for OpenGL +typedef struct +{ + int width,height; + int leftoffset,topoffset; + int lumpnum; +} patchnum_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 +{ + short x1, x2; + fixed_t gx, gy; // for line side calculation + fixed_t gz, gzt; // global bottom / top for silhouette clipping + fixed_t startfrac; // horizontal position of x1 + fixed_t scale; + fixed_t xiscale; // negative if flipped + fixed_t texturemid; + int patch; + uint_64_t mobjflags; + + // for color translation and shadow draw, maxbright frames as well + const lighttable_t *colormap; + + // killough 3/27/98: height sector for underwater/fake ceiling support + int heightsec; + + // [FG] colored blood and gibs + int color; +} vissprite_t; + +// +// Sprites are patches with a special naming convention +// so they can be recognized by R_InitSprites. +// The base name is NNNNFx or NNNNFxFx, with +// x indicating the rotation, x = 0, 1-7. +// The sprite and frame specified by a thing_t +// is range checked at run time. +// A sprite is a patch_t that is assumed to represent +// a three dimensional object and may have multiple +// rotations pre drawn. +// Horizontal flipping is used to save space, +// thus NNNNF2F5 defines a mirrored patch. +// Some sprites will only have one picture used +// for all views: NNNNF0 +// + +typedef struct +{ + // If false use 0 for any position. + // Note: as eight entries are available, + // we might as well insert the same name eight times. + int rotate; + + // Lump to use for view angles 0-7. + short lump[16]; + + // Flip bit (1 = flip) to use for view angles 0-15. + unsigned short flip; + +} 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? +// +// Go to http://classicgaming.com/doom/editing/ to find out -- killough +// + +typedef struct visplane +{ + struct visplane *next; // Next visplane in hash chain -- killough + int picnum, lightlevel, minx, maxx; + fixed_t height; + fixed_t xoffs, yoffs; // killough 2/28/98: Support scrolling flats + // e6y: resolution limitation is removed + // bottom and top arrays are dynamically + // allocated immediately after the visplane + unsigned short *bottom; + unsigned short pad1; // leave pads for [minx-1]/[maxx+1] + unsigned short top[3]; +} visplane_t; + +#endif diff --git a/src/r_demo.c b/src/r_demo.c new file mode 100644 index 0000000..8a59ee3 --- /dev/null +++ b/src/r_demo.c @@ -0,0 +1,1705 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, Andrey Budko + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Demo stuff + * + *--------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +#include +#include + +#include "doomdef.h" +#include "doomtype.h" +#include "doomstat.h" +#include "r_demo.h" +#include "r_fps.h" +#include "lprintf.h" +#include "i_system.h" +#include "i_video.h" +#include "m_misc.h" +#include "m_argv.h" +#include "w_wad.h" +#include "d_main.h" +#include "d_deh.h" +#include "g_game.h" +#include "p_map.h" +#include "hu_stuff.h" +#include "g_overflow.h" +#include "e6y.h" + +#include "m_io.h" + +int IsDemoPlayback(void) +{ + int p; + + if ((p = M_CheckParm("-playdemo")) && (p < myargc - 1)) + return p; + if ((p = M_CheckParm("-timedemo")) && (p < myargc - 1)) + return p; + if ((p = M_CheckParm("-fastdemo")) && (p < myargc - 1)) + return p; + + return 0; +} + +int IsDemoContinue(void) +{ + int p; + + if ((p = M_CheckParm("-recordfromto")) && (p < myargc - 2) && + I_FindFile(myargv[p + 1], ".lmp")) + { + return p; + } + + return 0; +} + +int LoadDemo(const char *name, const byte **buffer, int *length, int *lump) +{ + char basename[9]; + char *filename = NULL; + int num = -1; + int len = 0; + const byte *buf = NULL; + + ExtractFileBase(name, basename); + basename[8] = 0; + + // check ns_demos namespace first, then ns_global + num = (W_CheckNumForName)(basename, ns_demos); + if (num < 0) + { + num = W_CheckNumForName(basename); + } + + if (num < 0) + { + // Allow for demos not loaded as lumps + static byte *sbuf = NULL; + filename = I_FindFile(name, ".lmp"); + if (filename) + { + if (sbuf) + { + free(sbuf); + sbuf = NULL; + } + + len = M_ReadFile(filename, &sbuf); + buf = (const byte *)sbuf; + free(filename); + } + } + else + { + buf = W_CacheLumpNum(num); + len = W_LumpLength(num); + } + + if (len < 0) + len = 0; + + if (len > 0) + { + if (buffer) + *buffer = buf; + if (length) + *length = len; + if (lump) + *lump = num; + } + + return (len > 0); +} + +// +// Smooth playing stuff +// + +int demo_smoothturns = false; +int demo_smoothturnsfactor = 6; + +static int smooth_playing_turns[SMOOTH_PLAYING_MAXFACTOR]; +static int_64_t smooth_playing_sum; +static int smooth_playing_index; +static angle_t smooth_playing_angle; + +void R_SmoothPlaying_Reset(player_t *player) +{ + if (demo_smoothturns && demoplayback) + { + if (!player) + player = &players[displayplayer]; + + if (player==&players[displayplayer]) + { + if (player->mo) + { + smooth_playing_angle = player->mo->angle; + memset(smooth_playing_turns, 0, sizeof(smooth_playing_turns[0]) * SMOOTH_PLAYING_MAXFACTOR); + smooth_playing_sum = 0; + smooth_playing_index = 0; + } + } + } +} + +void R_SmoothPlaying_Add(int delta) +{ + if (demo_smoothturns && demoplayback) + { + smooth_playing_sum -= smooth_playing_turns[smooth_playing_index]; + smooth_playing_turns[smooth_playing_index] = delta; + smooth_playing_index = (smooth_playing_index + 1)%(demo_smoothturnsfactor); + smooth_playing_sum += delta; + smooth_playing_angle += (int)(smooth_playing_sum/(demo_smoothturnsfactor)); + } +} + +angle_t R_SmoothPlaying_Get(player_t *player) +{ + if (demo_smoothturns && demoplayback && player == &players[displayplayer]) + return smooth_playing_angle; + else + return player->mo->angle; +} + +void R_ResetAfterTeleport(player_t *player) +{ + R_ResetViewInterpolation(); + R_SmoothPlaying_Reset(player); +} + +// +// DemoEx stuff +// + +#ifdef HAVE_LIBPCREPOSIX +#include "pcreposix.h" +#endif + +#define PWAD_SIGNATURE "PWAD" +#define DEMOEX_VERSION "2" + +#define DEMOEX_VERSION_LUMPNAME "VERSION" +#define DEMOEX_PORTNAME_LUMPNAME "PORTNAME" +#define DEMOEX_PARAMS_LUMPNAME "CMDLINE" +#define DEMOEX_MLOOK_LUMPNAME "MLOOK" +#define DEMOEX_COMMENT_LUMPNAME "COMMENT" + +#define DEMOEX_SEPARATOR "\n" + +// patterns +int demo_patterns_count; +const char *demo_patterns_mask; +char **demo_patterns_list; +const char *demo_patterns_list_def[9]; + +// demo ex +int demo_extendedformat = -1; +int demo_extendedformat_default; +dboolean use_demoex_info = false; + +char demoex_filename[PATH_MAX]; +const char *demo_demoex_filename; +//wadtbl_t demoex; + +typedef struct +{ + const char name[9]; + short *data; + int lump; + size_t maxtick; + size_t tick; +} mlooklump_t; + +mlooklump_t mlook_lump = {DEMOEX_MLOOK_LUMPNAME, NULL, -2, 0, 0}; + +int AddString(char **str, const char *val); + +static void R_DemoEx_AddParams(wadtbl_t *wadtbl); +static int R_DemoEx_GetVersion(void); +static void R_DemoEx_GetParams(const byte *pwad_p, waddata_t *waddata); +static void R_DemoEx_AddMouseLookData(wadtbl_t *wadtbl); + +static int G_ReadDemoFooter(const char *filename); + +int AddString(char **str, const char *val) +{ + int size = 0; + + if (!str || !val) + return 0; + + if (*str) + { + size = strlen(*str) + strlen(val) + 1; + *str = realloc(*str, size); + strcat(*str, val); + } + else + { + size = strlen(val) + 1; + *str = malloc(size); + strcpy(*str, val); + } + + return size; +} + +void M_ChangeDemoExtendedFormat(void) +{ + if (demo_extendedformat == -1) + { + demo_extendedformat = demo_extendedformat_default; + } + + use_demoex_info = demo_extendedformat || M_CheckParm("-auto"); +} + +void W_InitPWADTable(wadtbl_t *wadtbl) +{ + //init header signature and lookup table offset and size + memcpy(wadtbl->header.identification, PWAD_SIGNATURE, 4); + wadtbl->header.infotableofs = sizeof(wadtbl->header); + wadtbl->header.numlumps = 0; + + //clear PWAD lookup table + wadtbl->lumps = NULL; + + //clear PWAD data + wadtbl->data = NULL; + wadtbl->datasize = 0; +} + +void W_FreePWADTable(wadtbl_t *wadtbl) +{ + //clear PWAD lookup table + free(wadtbl->lumps); + + //clear PWAD data + free(wadtbl->data); +} + +void W_AddLump(wadtbl_t *wadtbl, const char *name, const byte* data, size_t size) +{ + int lumpnum; + + if (!wadtbl || (name && strlen(name) > 8)) + { + I_Error("W_AddLump: wrong parameters."); + return; + } + + lumpnum = wadtbl->header.numlumps; + + if (name) + { + wadtbl->lumps = realloc(wadtbl->lumps, (lumpnum + 1) * sizeof(wadtbl->lumps[0])); + + memcpy(wadtbl->lumps[lumpnum].name, name, 8); + wadtbl->lumps[lumpnum].size = size; + wadtbl->lumps[lumpnum].filepos = wadtbl->header.infotableofs; + + wadtbl->header.numlumps++; + } + + if (data && size > 0) + { + wadtbl->data = realloc(wadtbl->data, wadtbl->datasize + size); + + memcpy(wadtbl->data + wadtbl->datasize, data, size); + wadtbl->datasize += size; + + wadtbl->header.infotableofs += size; + } +} + +void R_DemoEx_ShowComment(void) +{ + extern patchnum_t hu_font[]; + + int lump; + int cx = 10; + int cy = 10; + const char* ch; + int count; + int w; + + if (!use_demoex_info) + return; + + lump = W_CheckNumForName(DEMOEX_COMMENT_LUMPNAME); + if (lump == -1) + return; + + count = W_LumpLength(lump); + + if (count <= 0) + return; + + ch = W_CacheLumpNum(lump); + + for ( ; count ; count-- ) + { + int 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 = hu_font[c].width; + if (cx + w > SCREENWIDTH) + break; + + V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH); + cx += w; + } + + W_UnlockLumpNum(lump); +} + +angle_t R_DemoEx_ReadMLook(void) +{ + angle_t pitch; + + if (!use_demoex_info || !(demoplayback || democontinue)) + return 0; + + // mlook data must be initialised here + if ((mlook_lump.lump == -2)) + { + if (R_DemoEx_GetVersion() < 2) + { + // unsupported format + mlook_lump.lump = -1; + } + else + { + mlook_lump.lump = W_CheckNumForName(mlook_lump.name); + if (mlook_lump.lump != -1) + { + const unsigned char *data = W_CacheLumpName(mlook_lump.name); + int size = W_LumpLength(mlook_lump.lump); + + mlook_lump.maxtick = size / sizeof(mlook_lump.data[0]); + mlook_lump.data = malloc(size); + memcpy(mlook_lump.data, data, size); + } + } + } + + pitch = 0; + if (mlook_lump.data && mlook_lump.tick < mlook_lump.maxtick && + consoleplayer == displayplayer && !walkcamera.type) + { + pitch = mlook_lump.data[mlook_lump.tick]; + } + mlook_lump.tick++; + + return (pitch << 16); +} + +void R_DemoEx_ResetMLook(void) +{ + mlook_lump.tick = 0; +} + +void R_DemoEx_WriteMLook(angle_t pitch) +{ + if (!use_demoex_info || !demorecording) + return; + + if (mlook_lump.tick >= mlook_lump.maxtick) + { + int ticks = mlook_lump.maxtick; + mlook_lump.maxtick = (mlook_lump.maxtick ? mlook_lump.maxtick * 2 : 8192); + if (mlook_lump.tick >= mlook_lump.maxtick) + mlook_lump.maxtick = mlook_lump.tick * 2; + mlook_lump.data = realloc(mlook_lump.data, mlook_lump.maxtick * sizeof(mlook_lump.data[0])); + memset(mlook_lump.data + ticks, 0, (mlook_lump.maxtick - ticks) * sizeof(mlook_lump.data[0])); + } + + mlook_lump.data[mlook_lump.tick] = (short)(pitch >> 16); + mlook_lump.tick++; +} + +static int R_DemoEx_GetVersion(void) +{ + int result = -1; + + int lump, ver; + unsigned int size; + const char *data; + char str_ver[32]; + + lump = W_CheckNumForName(DEMOEX_VERSION_LUMPNAME); + if (lump != -1) + { + size = W_LumpLength(lump); + if (size > 0) + { + size_t len = MIN(size, sizeof(str_ver) - 1); + data = W_CacheLumpNum(lump); + strncpy(str_ver, data, len); + str_ver[len] = 0; + + if (sscanf(str_ver, "%d", &ver) == 1) + { + result = ver; + } + } + W_UnlockLumpNum(lump); + } + + return result; +} + +static void R_DemoEx_GetParams(const byte *pwad_p, waddata_t *waddata) +{ + int lump; + size_t size; + char *str; + const char *data; + char **params; + int i, p, paramscount; + + lump = W_CheckNumForName(DEMOEX_PARAMS_LUMPNAME); + if (lump == -1) + return; + + size = W_LumpLength(lump); + if (size <= 0) + return; + + str = calloc(size + 1, 1); + if (!str) + return; + + data = W_CacheLumpNum(lump); + strncpy(str, data, size); + + M_ParseCmdLine(str, NULL, NULL, ¶mscount, &i); + + params = malloc(paramscount * sizeof(char*) + i * sizeof(char) + 1); + if (params) + { + struct { + const char *param; + wad_source_t source; + } files[] = { + {"-iwad" , source_iwad}, + {"-file" , source_pwad}, + {"-deh" , source_deh}, + {NULL} + }; + + M_ParseCmdLine(str, params, ((char*)params) + sizeof(char*) * paramscount, ¶mscount, &i); + + if (!M_CheckParm("-iwad") && !M_CheckParm("-file")) + { + i = 0; + while (files[i].param) + { + p = M_CheckParmEx(files[i].param, params, paramscount); + if (p >= 0) + { + while (++p != paramscount && *params[p] != '-') + { + char *filename; + //something is wrong here + filename = I_FindFile(params[p], ".wad"); + if (!filename) + { + filename = strdup(params[p]); + } + WadDataAddItem(waddata, filename, files[i].source, 0); + free(filename); + } + } + i++; + } + } + + if (!M_CheckParm("-complevel")) + { + p = M_CheckParmEx("-complevel", params, paramscount); + if (p >= 0 && p < (int)paramscount - 1) + { + int level; + static char str[4]; + extern int G_GetNamedComplevel (const char *arg); + M_AddParam("-complevel"); + // [FG] only save numeric complevel values + level = G_GetNamedComplevel(params[p + 1]); + snprintf(str, sizeof(str), "%d", level); + M_AddParam(str); + } + } + + //for recording or playback using "single-player coop" mode + if (!M_CheckParm("-solo-net")) + { + p = M_CheckParmEx("-solo-net", params, paramscount); + if (p >= 0) + { + M_AddParam("-solo-net"); + } + } + + //for recording or playback using "coop in single-player" mode + if (!M_CheckParm("-coop_spawns")) + { + p = M_CheckParmEx("-coop_spawns", params, paramscount); + if (p >= 0) + { + M_AddParam("-coop_spawns"); + } + } + + if (!M_CheckParm("-emulate")) + { + p = M_CheckParmEx("-emulate", params, paramscount); + if (p >= 0 && p < (int)paramscount - 1) + { + M_AddParam("-emulate"); + M_AddParam(params[p + 1]); + } + } + + // for doom 1.2 + if (!M_CheckParm("-respawn")) + { + p = M_CheckParmEx("-respawn", params, paramscount); + if (p >= 0) + { + M_AddParam("-respawn"); + } + } + + // for doom 1.2 + if (!M_CheckParm("-fast")) + { + p = M_CheckParmEx("-fast", params, paramscount); + if (p >= 0) + { + M_AddParam("-fast"); + } + } + + // for doom 1.2 + if (!M_CheckParm("-nomonsters")) + { + p = M_CheckParmEx("-nomonsters", params, paramscount); + if (p >= 0) + { + M_AddParam("-nomonsters"); + } + } + + p = M_CheckParmEx("-spechit", params, paramscount); + if (p >= 0 && p < (int)paramscount - 1) + { + spechit_baseaddr = atoi(params[p + 1]); + } + + //overflows + { + overrun_list_t overflow; + for (overflow = 0; overflow < OVERFLOW_MAX; overflow++) + { + int value; + char *pstr, *mask; + + mask = malloc(strlen(overflow_cfgname[overflow]) + 16); + if (mask) + { + sprintf(mask, "-set %s", overflow_cfgname[overflow]); + pstr = strstr(str, mask); + + if (pstr) + { + strcat(mask, " = %d"); + if (sscanf(pstr, mask, &value) == 1) + { + overflows[overflow].footer = true; + overflows[overflow].footer_emulate = value; + } + } + free(mask); + } + } + } + + free(params); + } + + W_UnlockLumpNum(lump); + free(str); +} + +static void R_DemoEx_AddParams(wadtbl_t *wadtbl) +{ + size_t i; + int p; + char buf[200]; + + char* filename_p; + char* fileext_p; + + char *files = NULL; + char *iwad = NULL; + char *pwads = NULL; + char *dehs = NULL; + char **item; + + //iwad and pwads + for (i = 0; i < numwadfiles; i++) + { + filename_p = PathFindFileName(wadfiles[i].name); + fileext_p = filename_p + strlen(filename_p) - 1; + while (fileext_p != filename_p && *(fileext_p - 1) != '.') + fileext_p--; + if (fileext_p == filename_p) + continue; + + item = NULL; + + if (wadfiles[i].src == source_iwad && !iwad && !strcasecmp(fileext_p, "wad")) + item = &iwad; + + if (wadfiles[i].src == source_pwad && !strcasecmp(fileext_p, "wad")) + item = &pwads; + + if (item) + { + AddString(item, "\""); + AddString(item, filename_p); + AddString(item, "\" "); + } + } + + //dehs + p = M_CheckParm ("-deh"); + if (p) + { + while (++p != myargc && *myargv[p] != '-') + { + char *file = NULL; + if ((file = I_FindFile(myargv[p], ".bex")) || + (file = I_FindFile(myargv[p], ".deh"))) + { + filename_p = PathFindFileName(file); + AddString(&dehs, "\""); + AddString(&dehs, filename_p); + AddString(&dehs, "\" "); + free(file); + } + } + } + + if (iwad) + { + AddString(&files, "-iwad "); + AddString(&files, iwad); + } + + if (pwads) + { + AddString(&files, "-file "); + AddString(&files, pwads); + } + + if (dehs) + { + AddString(&files, "-deh "); + AddString(&files, dehs); + } + + //add complevel for formats which do not have it in header + if (demo_compatibility) + { + sprintf(buf, "-complevel %d ", compatibility_level); + AddString(&files, buf); + } + + //for recording or playback using "single-player coop" mode + if (M_CheckParm("-solo-net")) + { + sprintf(buf, "-solo-net "); + AddString(&files, buf); + } + + //for recording or playback using "coop in single-player" mode + if (M_CheckParm("-coop_spawns")) + { + sprintf(buf, "-coop_spawns "); + AddString(&files, buf); + } + + if ((p = M_CheckParm("-emulate")) && (p < myargc - 1)) + { + sprintf(buf, "-emulate %s", myargv[p + 1]); + AddString(&files, buf); + } + + // doom 1.2 does not store these params in header + if (compatibility_level == doom_12_compatibility) + { + if (M_CheckParm("-respawn")) + { + sprintf(buf, "-respawn "); + AddString(&files, buf); + } + if (M_CheckParm("-fast")) + { + sprintf(buf, "-fast "); + AddString(&files, buf); + } + if (M_CheckParm("-nomonsters")) + { + sprintf(buf, "-nomonsters "); + AddString(&files, buf); + } + } + + if (spechit_baseaddr != 0 && spechit_baseaddr != DEFAULT_SPECHIT_MAGIC) + { + sprintf(buf, "-spechit %d ", spechit_baseaddr); + AddString(&files, buf); + } + + //overflows + { + overrun_list_t overflow; + for (overflow = 0; overflow < OVERFLOW_MAX; overflow++) + { + if (overflows[overflow].shit_happens) + { + sprintf(buf, "-set %s=%d ", overflow_cfgname[overflow], overflows[overflow].emulate); + AddString(&files, buf); + } + } + } + + if (files) + { + W_AddLump(wadtbl, DEMOEX_PARAMS_LUMPNAME, (const byte*)files, strlen(files)); + } +} + +static void R_DemoEx_AddMouseLookData(wadtbl_t *wadtbl) +{ + int i = 0; + + if (!mlook_lump.data) + return; + + // search for at least one tic with a nonzero pitch + while (i < (int)mlook_lump.tick) + { + if (mlook_lump.data[i] != 0) + { + W_AddLump(wadtbl, mlook_lump.name, + (const byte*)mlook_lump.data, mlook_lump.tick * sizeof(mlook_lump.data[0])); + break; + } + i++; + } +} + +void I_DemoExShutdown(void) +{ + W_ReleaseAllWads(); + + if (demoex_filename[0] && !(demo_demoex_filename && *demo_demoex_filename)) + { + lprintf(LO_DEBUG, "I_DemoExShutdown: removing %s\n", demoex_filename); + if (unlink(demoex_filename) != 0) + { + lprintf(LO_DEBUG, "I_DemoExShutdown: %s\n", strerror(errno)); + } + } +} + +byte* G_GetDemoFooter(const char *filename, const byte **footer, size_t *size) +{ + byte* result = NULL; + + FILE *hfile; + byte *buffer = NULL; + const byte* p; + size_t file_size; + + hfile = M_fopen(filename, "rb"); + + if (!hfile) + return result; + + //get demo size in bytes + fseek(hfile, 0, SEEK_END); + file_size = ftell(hfile); + fseek(hfile, 0, SEEK_SET); + + buffer = malloc(file_size); + + if (fread(buffer, file_size, 1, hfile) == 1) + { + //skip demo header + p = G_ReadDemoHeaderEx(buffer, file_size, RDH_SKIP_HEADER); + + //skip demo data + while (p < buffer + file_size && *p != DEMOMARKER) + { + p += bytes_per_tic; + } + + if (*p == DEMOMARKER) + { + //skip DEMOMARKER + p++; + + //seach for the "PWAD" signature after ENDDEMOMARKER + while (p - buffer + sizeof(wadinfo_t) < file_size) + { + if (!memcmp(p, PWAD_SIGNATURE, strlen(PWAD_SIGNATURE))) + { + //got it! + //the demo has an additional information itself + int demoex_size = file_size - (p - buffer); + + result = buffer; + + if (footer) + { + *footer = p; + } + + if (size) + { + *size = demoex_size; + } + + break; + } + p++; + } + } + } + + fclose(hfile); + + return result; +} + +void G_SetDemoFooter(const char *filename, wadtbl_t *wadtbl) +{ + FILE *hfile; + byte *buffer = NULL; + const byte *demoex_p = NULL; + size_t size; + + buffer = G_GetDemoFooter(filename, &demoex_p, &size); + if (buffer) + { + char newfilename[PATH_MAX]; + + strncpy(newfilename, filename, sizeof(newfilename) - 5); + newfilename[sizeof(newfilename) - 5] = 0; + strcat(newfilename, ".out"); + + hfile = M_fopen(newfilename, "wb"); + if (hfile) + { + int demosize = (demoex_p - buffer); + int headersize = sizeof(wadtbl->header); + int datasize = wadtbl->datasize; + int lumpssize = wadtbl->header.numlumps * sizeof(wadtbl->lumps[0]); + + //write pwad header, all data and lookup table to the end of a demo + if ( + fwrite(buffer, demosize, 1, hfile) != 1 || + fwrite(&wadtbl->header, headersize, 1, hfile) != 1 || + fwrite(wadtbl->data, datasize, 1, hfile) != 1 || + fwrite(wadtbl->lumps, lumpssize, 1, hfile) != 1 || + false) + { + I_Error("G_SetDemoFooter: error writing"); + } + + fclose(hfile); + } + free(buffer); + } +} + +int CheckWadBufIntegrity(const char *buffer, size_t size) +{ + int i; + unsigned int length; + wadinfo_t *header; + filelump_t *fileinfo; + int result = false; + + if (buffer && size > sizeof(*header)) + { + header = (wadinfo_t*)buffer; + if (strncmp(header->identification, "IWAD", 4) == 0 || + strncmp(header->identification, "PWAD", 4) == 0) + { + header->numlumps = LittleLong(header->numlumps); + header->infotableofs = LittleLong(header->infotableofs); + length = header->numlumps * sizeof(filelump_t); + + if (header->infotableofs + length <= size) + { + fileinfo = (filelump_t*)(buffer + header->infotableofs); + for (i = 0; i < header->numlumps; i++, fileinfo++) + { + if (fileinfo->filepos < 0 || + fileinfo->filepos > header->infotableofs || + fileinfo->filepos + fileinfo->size > header->infotableofs) + { + break; + } + } + result = (i == header->numlumps); + } + } + } + + return result; +} + +int CheckWadFileIntegrity(const char *filename) +{ + FILE *hfile; + int i; + unsigned int length; + wadinfo_t header; + filelump_t *fileinfo, *fileinfo2free = NULL; + int result = false; + + hfile = M_fopen(filename, "rb"); + if (hfile) + { + if (fread(&header, sizeof(header), 1, hfile) == 1 && + (strncmp(header.identification, "IWAD", 4) == 0 || + strncmp(header.identification, "PWAD", 4) == 0)) + { + header.numlumps = LittleLong(header.numlumps); + header.infotableofs = LittleLong(header.infotableofs); + length = header.numlumps * sizeof(filelump_t); + + fileinfo2free = fileinfo = malloc(length); + if (fileinfo) + { + if (fseek(hfile, header.infotableofs, SEEK_SET) == 0 && + fread(fileinfo, length, 1, hfile) == 1) + { + for (i = 0; i < header.numlumps; i++, fileinfo++) + { + if (fileinfo->filepos < 0 || + fileinfo->filepos > header.infotableofs || + fileinfo->filepos + fileinfo->size > header.infotableofs) + { + break; + } + } + result = (i == header.numlumps); + } + free(fileinfo2free); + } + } + fclose(hfile); + } + + return result; +} + +static int G_ReadDemoFooter(const char *filename) +{ + int result = false; + + byte *buffer = NULL; + const byte *demoex_p = NULL; + size_t size; + + M_ChangeDemoExtendedFormat(); + + if (!use_demoex_info) + return result; + + demoex_filename[0] = 0; + + if (demo_demoex_filename && *demo_demoex_filename) + { + strncpy(demoex_filename, demo_demoex_filename, PATH_MAX-1); + } + else + { + const char* tmp_dir; + char* tmp_path = NULL; + const char* template_format = "%sprboom-plus-demoex-XXXXXX"; + + tmp_dir = I_GetTempDir(); + if (tmp_dir && *tmp_dir != '\0') + { + tmp_path = malloc(strlen(tmp_dir) + 2); + strcpy(tmp_path, tmp_dir); + if (!HasTrailingSlash(tmp_dir)) + { + strcat(tmp_path, "/"); + } + + doom_snprintf(demoex_filename, sizeof(demoex_filename), template_format, tmp_path); +#ifdef HAVE_MKSTEMP + if (mkstemp(demoex_filename) == -1) + { + demoex_filename[0] = 0; + } +#else + mktemp(demoex_filename); +#endif + + free(tmp_path); + } + } + + if (!demoex_filename[0]) + { + lprintf(LO_ERROR, "G_ReadDemoFooter: failed to create demoex temp file"); + } + else + { + AddDefaultExtension(demoex_filename, ".wad"); + + buffer = G_GetDemoFooter(filename, &demoex_p, &size); + if (buffer) + { + //the demo has an additional information itself + size_t i; + waddata_t waddata; + + if (!CheckWadBufIntegrity(demoex_p, size)) + { + lprintf(LO_ERROR, "G_ReadDemoFooter: demo footer is corrupted\n"); + } + else + //write an additional info from a demo to demoex.wad + if (!M_WriteFile(demoex_filename, demoex_p, size)) + { + lprintf(LO_ERROR, "G_ReadDemoFooter: failed to create demoex temp file %s\n", demoex_filename); + } + else + { + //add demoex.wad to the wads list + D_AddFile(demoex_filename, source_auto_load); + + //cache demoex.wad for immediately getting its data with W_CacheLumpName + W_Init(); + + WadDataInit(&waddata); + + //enumerate and save all auto-loaded files and demo for future use + for (i = 0; i < numwadfiles; i++) + { + if ( + wadfiles[i].src == source_auto_load || + wadfiles[i].src == source_pre || + wadfiles[i].src == source_lmp) + { + WadDataAddItem(&waddata, wadfiles[i].name, wadfiles[i].src, 0); + } + } + + //get needed wads and dehs from demoex.wad + //restore all critical params like -spechit x + R_DemoEx_GetParams(buffer, &waddata); + + //replace old wadfiles with the new ones + if (waddata.numwadfiles) + { + for (i = 0; (size_t)i < waddata.numwadfiles; i++) + { + if (waddata.wadfiles[i].src == source_iwad) + { + W_ReleaseAllWads(); + WadDataToWadFiles(&waddata); + result = true; + break; + } + } + } + WadDataFree(&waddata); + } + free(buffer); + } + else + { + demoex_filename[0] = 0; + } + } + + return result; +} + +void G_WriteDemoFooter(FILE *file) +{ + wadtbl_t demoex; + + if (!use_demoex_info) + return; + + //init PWAD header + W_InitPWADTable(&demoex); + + // + //write all the data + // + + // separators for eye-friendly looking + W_AddLump(&demoex, NULL, (const byte*)DEMOEX_SEPARATOR, strlen(DEMOEX_SEPARATOR)); + W_AddLump(&demoex, NULL, (const byte*)DEMOEX_SEPARATOR, strlen(DEMOEX_SEPARATOR)); + + //process format version + W_AddLump(&demoex, DEMOEX_VERSION_LUMPNAME, (const byte*)DEMOEX_VERSION, strlen(DEMOEX_VERSION)); + W_AddLump(&demoex, NULL, (const byte*)DEMOEX_SEPARATOR, strlen(DEMOEX_SEPARATOR)); + + //process mlook + R_DemoEx_AddMouseLookData(&demoex); + W_AddLump(&demoex, NULL, (const byte*)DEMOEX_SEPARATOR, strlen(DEMOEX_SEPARATOR)); + + //process port name + W_AddLump(&demoex, DEMOEX_PORTNAME_LUMPNAME, + (const byte*)(PACKAGE_NAME" "PACKAGE_VERSION), strlen(PACKAGE_NAME" "PACKAGE_VERSION)); + W_AddLump(&demoex, NULL, (const byte*)DEMOEX_SEPARATOR, strlen(DEMOEX_SEPARATOR)); + + //process iwad, pwads, dehs and critical for demos params like -spechit, etc + R_DemoEx_AddParams(&demoex); + W_AddLump(&demoex, NULL, (const byte*)DEMOEX_SEPARATOR, strlen(DEMOEX_SEPARATOR)); + + //write pwad header, all data and lookup table to the end of a demo + if ( + fwrite(&demoex.header, sizeof(demoex.header), 1, file) != 1 || + fwrite(demoex.data, demoex.datasize, 1, file) != 1 || + fwrite(demoex.lumps, demoex.header.numlumps * sizeof(demoex.lumps[0]), 1, file) != 1 || + false) + { + I_Error("G_WriteDemoFooter: error writing"); + } + + W_FreePWADTable(&demoex); +} + +int WadDataInit(waddata_t *waddata) +{ + if (!waddata) + return false; + + memset(waddata, 0, sizeof(*waddata)); + return true; +} + +void WadDataFree(waddata_t *waddata) +{ + if (waddata) + { + if (waddata->wadfiles) + { + int i; + for (i = 0; i < (int)waddata->numwadfiles; i++) + { + if (waddata->wadfiles[i].name) + { + free(waddata->wadfiles[i].name); + waddata->wadfiles[i].name = NULL; + } + } + free(waddata->wadfiles); + waddata->wadfiles = NULL; + } + } +} + +int WadDataAddItem(waddata_t *waddata, const char *filename, wad_source_t source, int handle) +{ + if (!waddata || !filename) + return false; + + waddata->wadfiles = realloc(waddata->wadfiles, sizeof(*wadfiles) * (waddata->numwadfiles + 1)); + waddata->wadfiles[waddata->numwadfiles].name = strdup(filename); + waddata->wadfiles[waddata->numwadfiles].src = source; + waddata->wadfiles[waddata->numwadfiles].handle = handle; + + waddata->numwadfiles++; + + return true; +} + +int ParseDemoPattern(const char *str, waddata_t* waddata, char **missed, dboolean trytodownload) +{ + int processed = 0; + wadfile_info_t *wadfiles = NULL; + size_t numwadfiles = 0; + char *pStr = strdup(str); + char *pToken = pStr; + + if (missed) + { + *missed = NULL; + } + + for (;(pToken = strtok(pToken,"|"));pToken = NULL) + { + char *token = NULL; + processed++; + + if (trytodownload && !I_FindFile2(pToken, ".wad")) + { + D_TryGetWad(pToken); + } +#ifdef _MSC_VER + token = malloc(PATH_MAX); + if (GetFullPath(pToken, ".wad", token, PATH_MAX)) +#else + if ((token = I_FindFile(pToken, ".wad"))) +#endif + { + wadfiles = realloc(wadfiles, sizeof(*wadfiles)*(numwadfiles+1)); + wadfiles[numwadfiles].name = token; + wadfiles[numwadfiles].handle = 0; + + if (pToken == pStr) + { + wadfiles[numwadfiles].src = source_iwad; + } + else + { + char *p = (char*)wadfiles[numwadfiles].name; + int len = strlen(p); + if (!strcasecmp(&p[len-4],".wad")) + wadfiles[numwadfiles].src = source_pwad; + if (!strcasecmp(&p[len-4],".deh") || !strcasecmp(&p[len-4],".bex")) + wadfiles[numwadfiles].src = source_deh; + } + numwadfiles++; + } + else + { + if (missed) + { + int len = (*missed ? strlen(*missed) : 0); + *missed = realloc(*missed, len + strlen(pToken) + 100); + sprintf(*missed + len, " %s not found\n", pToken); + } + } + } + + WadDataFree(waddata); + + waddata->wadfiles = wadfiles; + waddata->numwadfiles = numwadfiles; + + free(pStr); + + return processed; +} + +#ifdef HAVE_LIBPCREPOSIX +int DemoNameToWadData(const char * demoname, waddata_t *waddata, patterndata_t *patterndata) +{ + int numwadfiles_required = 0; + int i; + size_t maxlen = 0; + char *pattern; + + char *demofilename = PathFindFileName(demoname); + + WadDataInit(waddata); + + for (i = 0; i < demo_patterns_count; i++) + { + if (strlen(demo_patterns_list[i]) > maxlen) + maxlen = strlen(demo_patterns_list[i]); + } + + pattern = malloc(maxlen + sizeof(char)); + for (i = 0; i < demo_patterns_count; i++) + { + int result; + regex_t preg; + regmatch_t pmatch[4]; + char errbuf[256]; + char *buf = demo_patterns_list[i]; + + regcomp(&preg, "(.*?)\\/(.*)\\/(.+)", REG_ICASE); + result = regexec(&preg, buf, 4, &pmatch[0], REG_NOTBOL); + regerror(result, &preg, errbuf, sizeof(errbuf)); + regfree(&preg); + + if (result != 0) + { + lprintf(LO_WARN, "Incorrect format of the <%s%d = \"%s\"> config entry\n", demo_patterns_mask, i, buf); + } + else + { + regmatch_t demo_match[2]; + int len = pmatch[2].rm_eo - pmatch[2].rm_so; + + strncpy(pattern, buf + pmatch[2].rm_so, len); + pattern[len] = '\0'; + result = regcomp(&preg, pattern, REG_ICASE); + if (result != 0) + { + regerror(result, &preg, errbuf, sizeof(errbuf)); + lprintf(LO_WARN, "Incorrect regular expressions in the <%s%d = \"%s\"> config entry - %s\n", demo_patterns_mask, i, buf, errbuf); + } + else + { + result = regexec(&preg, demofilename, 1, &demo_match[0], 0); + if (result == 0 && demo_match[0].rm_so == 0 && demo_match[0].rm_eo == (int)strlen(demofilename)) + { + numwadfiles_required = ParseDemoPattern(buf + pmatch[3].rm_so, waddata, + (patterndata ? &patterndata->missed : NULL), true); + + waddata->wadfiles = realloc(waddata->wadfiles, sizeof(*wadfiles)*(waddata->numwadfiles+1)); + waddata->wadfiles[waddata->numwadfiles].name = strdup(demoname); + waddata->wadfiles[waddata->numwadfiles].src = source_lmp; + waddata->wadfiles[waddata->numwadfiles].handle = 0; + waddata->numwadfiles++; + + if (patterndata) + { + len = MIN(pmatch[1].rm_eo - pmatch[1].rm_so, sizeof(patterndata->pattern_name) - 1); + strncpy(patterndata->pattern_name, buf, len); + patterndata->pattern_name[len] = '\0'; + + patterndata->pattern_num = i; + } + + break; + } + } + regfree(&preg); + } + } + free(pattern); + + return numwadfiles_required; +} +#endif // HAVE_LIBPCREPOSIX + +void WadDataToWadFiles(waddata_t *waddata) +{ + void ProcessDehFile(const char *filename, const char *outfilename, int lumpnum); + const char *D_dehout(void); + + int i, iwadindex = -1; + + wadfile_info_t *old_wadfiles=NULL; + size_t old_numwadfiles = numwadfiles; + + old_numwadfiles = numwadfiles; + old_wadfiles = malloc(sizeof(*(wadfiles)) * numwadfiles); + memcpy(old_wadfiles, wadfiles, sizeof(*(wadfiles)) * numwadfiles); + + free(wadfiles); + wadfiles = NULL; + numwadfiles = 0; + + for (i = 0; (size_t)i < waddata->numwadfiles; i++) + { + if (waddata->wadfiles[i].src == source_iwad) + { + AddIWAD(I_FindFile(waddata->wadfiles[i].name, ".wad")); + iwadindex = i; + break; + } + } + + if (iwadindex == -1) + { + I_Error("WadDataToWadFiles: IWAD not found\n"); + } + + for (i = 0; (size_t)i < old_numwadfiles; i++) + { + if (old_wadfiles[i].src == source_auto_load || old_wadfiles[i].src == source_pre) + { + wadfiles = realloc(wadfiles, sizeof(*wadfiles)*(numwadfiles+1)); + wadfiles[numwadfiles].name = strdup(old_wadfiles[i].name); + wadfiles[numwadfiles].src = old_wadfiles[i].src; + wadfiles[numwadfiles].handle = old_wadfiles[i].handle; + numwadfiles++; + } + } + + for (i = 0; (size_t)i < waddata->numwadfiles; i++) + { + if (waddata->wadfiles[i].src == source_auto_load) + { + wadfiles = realloc(wadfiles, sizeof(*wadfiles)*(numwadfiles+1)); + wadfiles[numwadfiles].name = strdup(waddata->wadfiles[i].name); + wadfiles[numwadfiles].src = waddata->wadfiles[i].src; + wadfiles[numwadfiles].handle = waddata->wadfiles[i].handle; + numwadfiles++; + } + } + + for (i = 0; (size_t)i < waddata->numwadfiles; i++) + { + if (waddata->wadfiles[i].src == source_iwad && i != iwadindex) + { + D_AddFile(waddata->wadfiles[i].name, source_pwad); + modifiedgame = true; + } + if (waddata->wadfiles[i].src == source_pwad) + { + const char *file = I_FindFile2(waddata->wadfiles[i].name, ".wad"); + if (!file && D_TryGetWad(waddata->wadfiles[i].name)) + { + file = I_FindFile2(waddata->wadfiles[i].name, ".wad"); + if (file) + { + free(waddata->wadfiles[i].name); + waddata->wadfiles[i].name = strdup(file); + } + } + if (file) + { + D_AddFile(waddata->wadfiles[i].name, source_pwad); + modifiedgame = true; + } + } + if (waddata->wadfiles[i].src == source_deh) + { + ProcessDehFile(waddata->wadfiles[i].name, D_dehout(), 0); + } + } + + for (i = 0; (size_t)i < waddata->numwadfiles; i++) + { + if (waddata->wadfiles[i].src == source_lmp || waddata->wadfiles[i].src == source_net) + D_AddFile(waddata->wadfiles[i].name, waddata->wadfiles[i].src); + } + + free(old_wadfiles); +} + +void WadFilesToWadData(waddata_t *waddata) +{ + int i; + + if (!waddata) + return; + + for (i = 0; i < (int)numwadfiles; i++) + { + WadDataAddItem(waddata, wadfiles[i].name, wadfiles[i].src, wadfiles[i].handle); + } +} + +int CheckDemoExDemo(void) +{ + int result = false; + int p; + + M_ChangeDemoExtendedFormat(); + + p = IsDemoPlayback(); + if (!p) + { + p = IsDemoContinue(); + } + + if (p) + { + char *demoname, *filename; + + filename = malloc(strlen(myargv[p + 1]) + 16); + strcpy(filename, myargv[p + 1]); + AddDefaultExtension(filename, ".lmp"); + + demoname = I_FindFile(filename, NULL); + if (demoname) + { + result = G_ReadDemoFooter(demoname); + free(demoname); + } + + free(filename); + } + + return result; +} + +int CheckAutoDemo(void) +{ + int result = false; + if (M_CheckParm("-auto")) +#ifndef HAVE_LIBPCREPOSIX + I_Error("Cannot process -auto - " + PACKAGE_NAME " was compiled without LIBPCRE support"); +#else + { + int i; + waddata_t waddata; + + for (i = 0; (size_t)i < numwadfiles; i++) + { + if (wadfiles[i].src == source_lmp) + { + int numwadfiles_required; + + patterndata_t patterndata; + memset(&patterndata, 0, sizeof(patterndata)); + + numwadfiles_required = DemoNameToWadData(wadfiles[i].name, &waddata, &patterndata); + + if (waddata.numwadfiles) + { + result = true; + if ((size_t)numwadfiles_required + 1 != waddata.numwadfiles && patterndata.missed) + { + I_Warning( + "DataAutoload: pattern #%i is used\n" + "%s not all required files are found, may not work\n", + patterndata.pattern_num, patterndata.missed); + } + else + { + lprintf(LO_WARN,"DataAutoload: pattern #%i is used\n", patterndata.pattern_num); + } + WadDataToWadFiles(&waddata); + } + free(patterndata.missed); + WadDataFree(&waddata); + break; + } + } + } +#endif // HAVE_LIBPCREPOSIX + + return result; +} + +const char *getwad_cmdline; + +dboolean D_TryGetWad(const char* name) +{ + dboolean result = false; + + char wadname[PATH_MAX]; + char* cmdline = NULL; + char* wadname_p = NULL; + char* msg = NULL; + const char* format = + "The necessary wad has not been found\n" + "Do you want to search for \'%s\'?\n\n" + "Command line:\n%s\n\n" + "Be careful! Execution of an unknown program is unsafe."; + + if (!getwad_cmdline || !name || !(*getwad_cmdline) || !(*name)) + return false; + + strncpy(wadname, PathFindFileName(name), sizeof(wadname) - 4); + AddDefaultExtension(wadname, ".wad"); + + cmdline = malloc(strlen(getwad_cmdline) + strlen(wadname) + 2); + wadname_p = strstr(getwad_cmdline, "%wadname%"); + if (wadname_p) + { + strncpy(cmdline, getwad_cmdline, wadname_p - getwad_cmdline); + strcat(cmdline, wadname); + strcat(cmdline, wadname_p + strlen("%wadname%")); + } + else + { + sprintf(cmdline, "%s %s", getwad_cmdline, wadname); + } + + msg = malloc(strlen(format) + strlen(wadname) + strlen(cmdline)); + sprintf(msg, format, wadname, cmdline); + + if (PRB_IDYES == I_MessageBox(msg, PRB_MB_DEFBUTTON2 | PRB_MB_YESNO)) + { + int ret; + + lprintf(LO_INFO, "D_TryGetWad: Trying to get %s from somewhere\n", name); + + ret = system(cmdline); + + if (ret != 0) + { + lprintf(LO_ERROR, "D_TryGetWad: Execution failed - %s\n", strerror(errno)); + } + else + { + char *str = I_FindFile(name, ".wad"); + if (str) + { + lprintf(LO_INFO, "D_TryGetWad: Successfully received\n"); + free(str); + result = true; + } + } + } + + free(msg); + free(cmdline); + + return result; +} diff --git a/src/r_demo.h b/src/r_demo.h new file mode 100644 index 0000000..4215734 --- /dev/null +++ b/src/r_demo.h @@ -0,0 +1,124 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, Andrey Budko + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Demo stuff + * + *--------------------------------------------------------------------- + */ + +#ifndef __R_DEMO__ +#define __R_DEMO__ + +#include "doomdef.h" +#include "doomtype.h" +#include "tables.h" +#include "d_player.h" +#include "w_wad.h" + +// +// Smooth playing stuff +// + +#define SMOOTH_PLAYING_MAXFACTOR 16 + +extern int demo_smoothturns; +extern int demo_smoothturnsfactor; + +void R_SmoothPlaying_Reset(player_t *player); +void R_SmoothPlaying_Add(int delta); +angle_t R_SmoothPlaying_Get(player_t *player); +void R_ResetAfterTeleport(player_t *player); + +// +// DemoEx stuff +// + +typedef struct +{ + wadinfo_t header; + filelump_t *lumps; + char* data; + int datasize; +} wadtbl_t; + +typedef struct +{ + wadfile_info_t *wadfiles; + size_t numwadfiles; +} waddata_t; + +typedef struct +{ + int pattern_num; + char pattern_name[80]; + char *missed; +} patterndata_t; + +extern int demo_extendedformat; +extern int demo_extendedformat_default; +extern const char *demo_demoex_filename; + +extern int demo_patterns_count; +extern const char *demo_patterns_mask; +extern char **demo_patterns_list; +extern const char *demo_patterns_list_def[]; + +extern const char *getwad_cmdline; + +int WadDataInit(waddata_t *waddata); +int WadDataAddItem(waddata_t *waddata, const char *filename, wad_source_t source, int handle); +void WadDataFree(waddata_t *wadfiles); + +int CheckDemoExDemo(void); +int CheckAutoDemo(void); +int ParseDemoPattern(const char *str, waddata_t* waddata, char **missed, dboolean trytodownload); +int DemoNameToWadData(const char * demoname, waddata_t *waddata, patterndata_t *patterndata); +void WadDataToWadFiles(waddata_t *waddata); +void WadFilesToWadData(waddata_t *waddata); + +void M_ChangeDemoExtendedFormat(void); + +byte* G_GetDemoFooter(const char *filename, const byte **footer, size_t *size); +void G_SetDemoFooter(const char *filename, wadtbl_t *wadtbl); +void G_WriteDemoFooter(FILE *file); +void I_DemoExShutdown(void); + +extern dboolean use_demoex_info; +void R_DemoEx_WriteMLook(angle_t pitch); +angle_t R_DemoEx_ReadMLook(void); +void R_DemoEx_ResetMLook(void); + +dboolean D_TryGetWad(const char* name); + +int IsDemoPlayback(void); +int IsDemoContinue(void); + +int LoadDemo(const char *name, const byte **buffer, int *length, int *lump); + +#endif // __R_DEMO__ diff --git a/src/r_draw.c b/src/r_draw.c new file mode 100644 index 0000000..1f2cab9 --- /dev/null +++ b/src/r_draw.c @@ -0,0 +1,1189 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * The actual span/column drawing functions. + * Here find the main potential for optimization, + * e.g. inline assembly, different algorithms. + * + *-----------------------------------------------------------------------------*/ + +#include + +#include "doomstat.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_draw.h" +#include "r_filter.h" +#include "v_video.h" +#include "st_stuff.h" +#include "g_game.h" +#include "am_map.h" +#include "lprintf.h" + +// +// All drawing to the view buffer is accomplished in this file. +// The other refresh files only know about ccordinates, +// not the architecture of the frame buffer. +// Conveniently, the frame buffer is a linear one, +// and we need only the base address, +// and the total size == width*height*depth/8., +// + +byte *viewimage; +int viewwidth; +int scaledviewwidth; +int viewheight; +int viewwindowx; +int viewwindowy; + +// Color tables for different players, +// translate a limited part to another +// (color ramps used for suit colors). +// + +// CPhipps - made const*'s +const byte *tranmap; // translucency filter maps 256x256 // phares +const byte *main_tranmap; // killough 4/11/98 + +// +// R_DrawColumn +// Source is the top of the column to scale. +// + +// SoM: OPTIMIZE for ANYRES +typedef enum +{ + COL_NONE, + COL_OPAQUE, + COL_TRANS, + COL_FLEXTRANS, + COL_FUZZ, + COL_FLEXADD +} columntype_e; + +static int temp_x = 0; +static int tempyl[4], tempyh[4]; + +// e6y: resolution limitation is removed +static byte *byte_tempbuf; +static unsigned short *short_tempbuf; +static unsigned int *int_tempbuf; + +static int startx = 0; +static int temptype = COL_NONE; +static int commontop, commonbot; +static const byte *temptranmap = NULL; +// SoM 7-28-04: Fix the fuzz problem. +static const byte *tempfuzzmap; + +// +// Spectre/Invisibility. +// + +#define FUZZTABLE 50 +// proff 08/17/98: Changed for high-res +//#define FUZZOFF (SCREENWIDTH) +#define FUZZOFF 1 + +static const int fuzzoffset_org[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 +}; + +static int fuzzoffset[FUZZTABLE]; + +static int fuzzpos = 0; + +// render pipelines +#define RDC_STANDARD 1 +#define RDC_TRANSLUCENT 2 +#define RDC_TRANSLATED 4 +#define RDC_FUZZ 8 +// no color mapping +#define RDC_NOCOLMAP 16 +// filter modes +#define RDC_DITHERZ 32 +#define RDC_BILINEAR 64 +#define RDC_ROUNDED 128 + +draw_vars_t drawvars = { + NULL, // byte_topleft + NULL, // short_topleft + NULL, // int_topleft + 0, // byte_pitch + 0, // short_pitch + 0, // int_pitch + RDRAW_FILTER_POINT, // filterwall + RDRAW_FILTER_POINT, // filterfloor + RDRAW_FILTER_POINT, // filtersprite + RDRAW_FILTER_POINT, // filterz + RDRAW_FILTER_POINT, // filterpatch + + RDRAW_MASKEDCOLUMNEDGE_SQUARE, // sprite_edges + RDRAW_MASKEDCOLUMNEDGE_SQUARE, // patch_edges + + // 49152 = FRACUNIT * 0.75 + // 81920 = FRACUNIT * 1.25 + 49152 // mag_threshold +}; + +// +// Error functions that will abort if R_FlushColumns tries to flush +// columns without a column type. +// + +static void R_FlushWholeError(void) +{ + I_Error("R_FlushWholeColumns called without being initialized.\n"); +} + +static void R_FlushHTError(void) +{ + I_Error("R_FlushHTColumns called without being initialized.\n"); +} + +static void R_QuadFlushError(void) +{ + I_Error("R_FlushQuadColumn called without being initialized.\n"); +} + +static void (*R_FlushWholeColumns)(void) = R_FlushWholeError; +static void (*R_FlushHTColumns)(void) = R_FlushHTError; +static void (*R_FlushQuadColumn)(void) = R_QuadFlushError; + +static void R_FlushColumns(void) +{ + if(temp_x != 4 || commontop >= commonbot) + R_FlushWholeColumns(); + else + { + R_FlushHTColumns(); + R_FlushQuadColumn(); + } + temp_x = 0; +} + +// +// R_ResetColumnBuffer +// +// haleyjd 09/13/04: new function to call from main rendering loop +// which gets rid of the unnecessary reset of various variables during +// column drawing. +// +void R_ResetColumnBuffer(void) +{ + // haleyjd 10/06/05: this must not be done if temp_x == 0! + if(temp_x) + R_FlushColumns(); + temptype = COL_NONE; + R_FlushWholeColumns = R_FlushWholeError; + R_FlushHTColumns = R_FlushHTError; + R_FlushQuadColumn = R_QuadFlushError; +} + +#define R_DRAWCOLUMN_PIPELINE RDC_STANDARD +#define R_DRAWCOLUMN_PIPELINE_BITS 8 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole8 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT8 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad8 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_TRANSLUCENT +#define R_DRAWCOLUMN_PIPELINE_BITS 8 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeTL8 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTTL8 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadTL8 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_FUZZ +#define R_DRAWCOLUMN_PIPELINE_BITS 8 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz8 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz8 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz8 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_STANDARD +#define R_DRAWCOLUMN_PIPELINE_BITS 15 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad15 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_TRANSLUCENT +#define R_DRAWCOLUMN_PIPELINE_BITS 15 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeTL15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTTL15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadTL15 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_FUZZ +#define R_DRAWCOLUMN_PIPELINE_BITS 15 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz15 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_STANDARD +#define R_DRAWCOLUMN_PIPELINE_BITS 16 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole16 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT16 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad16 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_TRANSLUCENT +#define R_DRAWCOLUMN_PIPELINE_BITS 16 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeTL16 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTTL16 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadTL16 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_FUZZ +#define R_DRAWCOLUMN_PIPELINE_BITS 16 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz16 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz16 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz16 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_STANDARD +#define R_DRAWCOLUMN_PIPELINE_BITS 32 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole32 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT32 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad32 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_TRANSLUCENT +#define R_DRAWCOLUMN_PIPELINE_BITS 32 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeTL32 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTTL32 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadTL32 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_FUZZ +#define R_DRAWCOLUMN_PIPELINE_BITS 32 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz32 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz32 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz32 +#include "r_drawflush.inl" + +// +// R_DrawColumn +// + +// +// 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. +// + +byte *translationtables; + +#define R_DRAWCOLUMN_PIPELINE_TYPE RDC_PIPELINE_STANDARD +#define R_DRAWCOLUMN_PIPELINE_BASE RDC_STANDARD + +#define R_DRAWCOLUMN_PIPELINE_BITS 8 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawColumn8 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole8 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT8 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad8 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 15 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawColumn15 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad15 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 16 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawColumn16 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole16 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT16 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad16 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 32 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawColumn32 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole32 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT32 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad32 +#include "r_drawcolpipeline.inl" + +#undef R_DRAWCOLUMN_PIPELINE_BASE +#undef R_DRAWCOLUMN_PIPELINE_TYPE + +// Here is the version of R_DrawColumn that deals with translucent // phares +// textures and sprites. It's identical to R_DrawColumn except // | +// for the spot where the color index is stuffed into *dest. At // V +// that point, the existing color index and the new color index +// are mapped through the TRANMAP lump filters to get a new color +// index whose RGB values are the average of the existing and new +// colors. +// +// Since we're concerned about performance, the 'translucent or +// opaque' decision is made outside this routine, not down where the +// actual code differences are. + +#define R_DRAWCOLUMN_PIPELINE_TYPE RDC_PIPELINE_TRANSLUCENT +#define R_DRAWCOLUMN_PIPELINE_BASE RDC_TRANSLUCENT + +#define R_DRAWCOLUMN_PIPELINE_BITS 8 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTLColumn8 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeTL8 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTTL8 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadTL8 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 15 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTLColumn15 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeTL15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTTL15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadTL15 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 16 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTLColumn16 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeTL16 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTTL16 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadTL16 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 32 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTLColumn32 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeTL32 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTTL32 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadTL32 +#include "r_drawcolpipeline.inl" + +#undef R_DRAWCOLUMN_PIPELINE_BASE +#undef R_DRAWCOLUMN_PIPELINE_TYPE + +// +// R_DrawTranslatedColumn +// Used to draw player sprites +// with the green colorramp mapped to others. +// Could be used with different translation +// tables, e.g. the lighter colored version +// of the BaronOfHell, the HellKnight, uses +// identical sprites, kinda brightened up. +// + +#define R_DRAWCOLUMN_PIPELINE_TYPE RDC_PIPELINE_TRANSLATED +#define R_DRAWCOLUMN_PIPELINE_BASE RDC_TRANSLATED + +#define R_DRAWCOLUMN_PIPELINE_BITS 8 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTranslatedColumn8 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole8 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT8 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad8 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 15 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTranslatedColumn15 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad15 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 16 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTranslatedColumn16 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole16 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT16 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad16 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 32 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTranslatedColumn32 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole32 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT32 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad32 +#include "r_drawcolpipeline.inl" + +#undef R_DRAWCOLUMN_PIPELINE_BASE +#undef R_DRAWCOLUMN_PIPELINE_TYPE + +// +// 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. +// + +#define R_DRAWCOLUMN_PIPELINE_TYPE RDC_PIPELINE_FUZZ +#define R_DRAWCOLUMN_PIPELINE_BASE RDC_FUZZ + +#define R_DRAWCOLUMN_PIPELINE_BITS 8 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawFuzzColumn8 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz8 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz8 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz8 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 15 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawFuzzColumn15 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz15 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 16 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawFuzzColumn16 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz16 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz16 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz16 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 32 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawFuzzColumn32 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz32 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz32 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz32 +#include "r_drawcolpipeline.inl" + +#undef R_DRAWCOLUMN_PIPELINE_BASE +#undef R_DRAWCOLUMN_PIPELINE_TYPE + +static R_DrawColumn_f drawcolumnfuncs[VID_MODEMAX][RDRAW_FILTER_MAXFILTERS][RDRAW_FILTER_MAXFILTERS][RDC_PIPELINE_MAXPIPELINES] = { + { + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn8_PointUV, + R_DrawTLColumn8_PointUV, + R_DrawTranslatedColumn8_PointUV, + R_DrawFuzzColumn8_PointUV,}, + {R_DrawColumn8_LinearUV, + R_DrawTLColumn8_LinearUV, + R_DrawTranslatedColumn8_LinearUV, + R_DrawFuzzColumn8_LinearUV,}, + {R_DrawColumn8_RoundedUV, + R_DrawTLColumn8_RoundedUV, + R_DrawTranslatedColumn8_RoundedUV, + R_DrawFuzzColumn8_RoundedUV,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn8_PointUV_PointZ, + R_DrawTLColumn8_PointUV_PointZ, + R_DrawTranslatedColumn8_PointUV_PointZ, + R_DrawFuzzColumn8_PointUV_PointZ,}, + {R_DrawColumn8_LinearUV_PointZ, + R_DrawTLColumn8_LinearUV_PointZ, + R_DrawTranslatedColumn8_LinearUV_PointZ, + R_DrawFuzzColumn8_LinearUV_PointZ,}, + {R_DrawColumn8_RoundedUV_PointZ, + R_DrawTLColumn8_RoundedUV_PointZ, + R_DrawTranslatedColumn8_RoundedUV_PointZ, + R_DrawFuzzColumn8_RoundedUV_PointZ,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn8_PointUV_LinearZ, + R_DrawTLColumn8_PointUV_LinearZ, + R_DrawTranslatedColumn8_PointUV_LinearZ, + R_DrawFuzzColumn8_PointUV_LinearZ,}, + {R_DrawColumn8_LinearUV_LinearZ, + R_DrawTLColumn8_LinearUV_LinearZ, + R_DrawTranslatedColumn8_LinearUV_LinearZ, + R_DrawFuzzColumn8_LinearUV_LinearZ,}, + {R_DrawColumn8_RoundedUV_LinearZ, + R_DrawTLColumn8_RoundedUV_LinearZ, + R_DrawTranslatedColumn8_RoundedUV_LinearZ, + R_DrawFuzzColumn8_RoundedUV_LinearZ,}, + }, + }, + { + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn15_PointUV, + R_DrawTLColumn15_PointUV, + R_DrawTranslatedColumn15_PointUV, + R_DrawFuzzColumn15_PointUV,}, + {R_DrawColumn15_LinearUV, + R_DrawTLColumn15_LinearUV, + R_DrawTranslatedColumn15_LinearUV, + R_DrawFuzzColumn15_LinearUV,}, + {R_DrawColumn15_RoundedUV, + R_DrawTLColumn15_RoundedUV, + R_DrawTranslatedColumn15_RoundedUV, + R_DrawFuzzColumn15_RoundedUV,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn15_PointUV_PointZ, + R_DrawTLColumn15_PointUV_PointZ, + R_DrawTranslatedColumn15_PointUV_PointZ, + R_DrawFuzzColumn15_PointUV_PointZ,}, + {R_DrawColumn15_LinearUV_PointZ, + R_DrawTLColumn15_LinearUV_PointZ, + R_DrawTranslatedColumn15_LinearUV_PointZ, + R_DrawFuzzColumn15_LinearUV_PointZ,}, + {R_DrawColumn15_RoundedUV_PointZ, + R_DrawTLColumn15_RoundedUV_PointZ, + R_DrawTranslatedColumn15_RoundedUV_PointZ, + R_DrawFuzzColumn15_RoundedUV_PointZ,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn15_PointUV_LinearZ, + R_DrawTLColumn15_PointUV_LinearZ, + R_DrawTranslatedColumn15_PointUV_LinearZ, + R_DrawFuzzColumn15_PointUV_LinearZ,}, + {R_DrawColumn15_LinearUV_LinearZ, + R_DrawTLColumn15_LinearUV_LinearZ, + R_DrawTranslatedColumn15_LinearUV_LinearZ, + R_DrawFuzzColumn15_LinearUV_LinearZ,}, + {R_DrawColumn15_RoundedUV_LinearZ, + R_DrawTLColumn15_RoundedUV_LinearZ, + R_DrawTranslatedColumn15_RoundedUV_LinearZ, + R_DrawFuzzColumn15_RoundedUV_LinearZ,}, + }, + }, + { + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn16_PointUV, + R_DrawTLColumn16_PointUV, + R_DrawTranslatedColumn16_PointUV, + R_DrawFuzzColumn16_PointUV,}, + {R_DrawColumn16_LinearUV, + R_DrawTLColumn16_LinearUV, + R_DrawTranslatedColumn16_LinearUV, + R_DrawFuzzColumn16_LinearUV,}, + {R_DrawColumn16_RoundedUV, + R_DrawTLColumn16_RoundedUV, + R_DrawTranslatedColumn16_RoundedUV, + R_DrawFuzzColumn16_RoundedUV,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn16_PointUV_PointZ, + R_DrawTLColumn16_PointUV_PointZ, + R_DrawTranslatedColumn16_PointUV_PointZ, + R_DrawFuzzColumn16_PointUV_PointZ,}, + {R_DrawColumn16_LinearUV_PointZ, + R_DrawTLColumn16_LinearUV_PointZ, + R_DrawTranslatedColumn16_LinearUV_PointZ, + R_DrawFuzzColumn16_LinearUV_PointZ,}, + {R_DrawColumn16_RoundedUV_PointZ, + R_DrawTLColumn16_RoundedUV_PointZ, + R_DrawTranslatedColumn16_RoundedUV_PointZ, + R_DrawFuzzColumn16_RoundedUV_PointZ,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn16_PointUV_LinearZ, + R_DrawTLColumn16_PointUV_LinearZ, + R_DrawTranslatedColumn16_PointUV_LinearZ, + R_DrawFuzzColumn16_PointUV_LinearZ,}, + {R_DrawColumn16_LinearUV_LinearZ, + R_DrawTLColumn16_LinearUV_LinearZ, + R_DrawTranslatedColumn16_LinearUV_LinearZ, + R_DrawFuzzColumn16_LinearUV_LinearZ,}, + {R_DrawColumn16_RoundedUV_LinearZ, + R_DrawTLColumn16_RoundedUV_LinearZ, + R_DrawTranslatedColumn16_RoundedUV_LinearZ, + R_DrawFuzzColumn16_RoundedUV_LinearZ,}, + }, + }, + { + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn32_PointUV, + R_DrawTLColumn32_PointUV, + R_DrawTranslatedColumn32_PointUV, + R_DrawFuzzColumn32_PointUV,}, + {R_DrawColumn32_LinearUV, + R_DrawTLColumn32_LinearUV, + R_DrawTranslatedColumn32_LinearUV, + R_DrawFuzzColumn32_LinearUV,}, + {R_DrawColumn32_RoundedUV, + R_DrawTLColumn32_RoundedUV, + R_DrawTranslatedColumn32_RoundedUV, + R_DrawFuzzColumn32_RoundedUV,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn32_PointUV_PointZ, + R_DrawTLColumn32_PointUV_PointZ, + R_DrawTranslatedColumn32_PointUV_PointZ, + R_DrawFuzzColumn32_PointUV_PointZ,}, + {R_DrawColumn32_LinearUV_PointZ, + R_DrawTLColumn32_LinearUV_PointZ, + R_DrawTranslatedColumn32_LinearUV_PointZ, + R_DrawFuzzColumn32_LinearUV_PointZ,}, + {R_DrawColumn32_RoundedUV_PointZ, + R_DrawTLColumn32_RoundedUV_PointZ, + R_DrawTranslatedColumn32_RoundedUV_PointZ, + R_DrawFuzzColumn32_RoundedUV_PointZ,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn32_PointUV_LinearZ, + R_DrawTLColumn32_PointUV_LinearZ, + R_DrawTranslatedColumn32_PointUV_LinearZ, + R_DrawFuzzColumn32_PointUV_LinearZ,}, + {R_DrawColumn32_LinearUV_LinearZ, + R_DrawTLColumn32_LinearUV_LinearZ, + R_DrawTranslatedColumn32_LinearUV_LinearZ, + R_DrawFuzzColumn32_LinearUV_LinearZ,}, + {R_DrawColumn32_RoundedUV_LinearZ, + R_DrawTLColumn32_RoundedUV_LinearZ, + R_DrawTranslatedColumn32_RoundedUV_LinearZ, + R_DrawFuzzColumn32_RoundedUV_LinearZ,}, + }, + }, +}; + +R_DrawColumn_f R_GetDrawColumnFunc(enum column_pipeline_e type, + enum draw_filter_type_e filter, + enum draw_filter_type_e filterz) { + R_DrawColumn_f result = drawcolumnfuncs[V_GetMode()][filterz][filter][type]; + if (result == NULL) + I_Error("R_GetDrawColumnFunc: undefined function (%d, %d, %d)", + type, filter, filterz); + return result; +} + +void R_SetDefaultDrawColumnVars(draw_column_vars_t *dcvars) { + dcvars->x = dcvars->yl = dcvars->yh = dcvars->z = 0; + dcvars->iscale = dcvars->texturemid = dcvars->texheight = dcvars->texu = 0; + dcvars->source = dcvars->prevsource = dcvars->nextsource = NULL; + dcvars->colormap = dcvars->nextcolormap = colormaps[0]; + dcvars->translation = NULL; + dcvars->edgeslope = dcvars->drawingmasked = 0; + dcvars->edgetype = drawvars.sprite_edges; + dcvars->flags = 0; +} + +// +// R_InitTranslationTables +// Creates the translation tables to map +// the green color ramp to gray, brown, red. +// Assumes a given structure of the PLAYPAL. +// Could be read from a lump instead. +// + +byte playernumtotrans[MAXPLAYERS]; + +void R_InitTranslationTables (void) +{ + int i, j; +#define MAXTRANS 3 + byte transtocolour[MAXTRANS]; + byte player_colors[] = {0x70, 0x60, 0x40, 0x20}; + + // killough 5/2/98: + // Remove dependency of colormaps aligned on 256-byte boundary + + if (translationtables == NULL) // CPhipps - allow multiple calls + translationtables = Z_Malloc(256*MAXTRANS, PU_STATIC, 0); + + for (i=0; i= 0x70 && i<= 0x7f) + { + // CPhipps - configurable player colours + translationtables[i] = colormaps[0][(i&0xf) + transtocolour[0]]; + translationtables[i+256] = colormaps[0][(i&0xf) + transtocolour[1]]; + translationtables[i+512] = colormaps[0][(i&0xf) + transtocolour[2]]; + } + else // Keep all other colors as is. + translationtables[i]=translationtables[i+256]=translationtables[i+512]=i; +} + +// +// R_DrawSpan +// With DOOM style restrictions on view orientation, +// the floors and ceilings consist of horizontal slices +// or spans with constant z depth. +// However, rotation around the world z axis is possible, +// thus this mapping, while simpler and faster than +// perspective correct texture mapping, has to traverse +// the texture at an angle in all but a few cases. +// In consequence, flats are not stored by column (like walls), +// and the inner loop has to step in texture space u and v. +// + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan8_PointUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 8 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan8_PointUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 8 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan8_LinearUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 8 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan8_LinearUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 8 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan8_RoundedUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 8 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan8_RoundedUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 8 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_PointUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 15 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_PointUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 15 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_LinearUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 15 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_LinearUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 15 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_RoundedUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 15 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_RoundedUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 15 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan16_PointUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 16 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan16_PointUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 16 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan16_LinearUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 16 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan16_LinearUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 16 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan16_RoundedUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 16 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan16_RoundedUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 16 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan32_PointUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 32 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan32_PointUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 32 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan32_LinearUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 32 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan32_LinearUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 32 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan32_RoundedUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 32 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan32_RoundedUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 32 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED | RDC_DITHERZ) +#include "r_drawspan.inl" + +static R_DrawSpan_f drawspanfuncs[VID_MODEMAX][RDRAW_FILTER_MAXFILTERS][RDRAW_FILTER_MAXFILTERS] = { + { + { + NULL, + NULL, + NULL, + NULL, + }, + { + NULL, + R_DrawSpan8_PointUV_PointZ, + R_DrawSpan8_LinearUV_PointZ, + R_DrawSpan8_RoundedUV_PointZ, + }, + { + NULL, + R_DrawSpan8_PointUV_LinearZ, + R_DrawSpan8_LinearUV_LinearZ, + R_DrawSpan8_RoundedUV_LinearZ, + }, + { + NULL, + NULL, + NULL, + NULL, + }, + }, + { + { + NULL, + NULL, + NULL, + NULL, + }, + { + NULL, + R_DrawSpan15_PointUV_PointZ, + R_DrawSpan15_LinearUV_PointZ, + R_DrawSpan15_RoundedUV_PointZ, + }, + { + NULL, + R_DrawSpan15_PointUV_LinearZ, + R_DrawSpan15_LinearUV_LinearZ, + R_DrawSpan15_RoundedUV_LinearZ, + }, + { + NULL, + NULL, + NULL, + NULL, + }, + }, + { + { + NULL, + NULL, + NULL, + NULL, + }, + { + NULL, + R_DrawSpan16_PointUV_PointZ, + R_DrawSpan16_LinearUV_PointZ, + R_DrawSpan16_RoundedUV_PointZ, + }, + { + NULL, + R_DrawSpan16_PointUV_LinearZ, + R_DrawSpan16_LinearUV_LinearZ, + R_DrawSpan16_RoundedUV_LinearZ, + }, + { + NULL, + NULL, + NULL, + NULL, + }, + }, + { + { + NULL, + NULL, + NULL, + NULL, + }, + { + NULL, + R_DrawSpan32_PointUV_PointZ, + R_DrawSpan32_LinearUV_PointZ, + R_DrawSpan32_RoundedUV_PointZ, + }, + { + NULL, + R_DrawSpan32_PointUV_LinearZ, + R_DrawSpan32_LinearUV_LinearZ, + R_DrawSpan32_RoundedUV_LinearZ, + }, + { + NULL, + NULL, + NULL, + NULL, + }, + }, +}; + +R_DrawSpan_f R_GetDrawSpanFunc(enum draw_filter_type_e filter, + enum draw_filter_type_e filterz) { + R_DrawSpan_f result = drawspanfuncs[V_GetMode()][filterz][filter]; + if (result == NULL) + I_Error("R_GetDrawSpanFunc: undefined function (%d, %d)", + filter, filterz); + return result; +} + +void R_DrawSpan(draw_span_vars_t *dsvars) { + R_GetDrawSpanFunc(drawvars.filterfloor, drawvars.filterz)(dsvars); +} + +void R_InitBuffersRes(void) +{ + extern byte *solidcol; + + if (solidcol) free(solidcol); + if (byte_tempbuf) free(byte_tempbuf); + if (short_tempbuf) free(short_tempbuf); + if (int_tempbuf) free(int_tempbuf); + + solidcol = calloc(1, SCREENWIDTH * sizeof(*solidcol)); + byte_tempbuf = calloc(1, (SCREENHEIGHT * 4) * sizeof(*byte_tempbuf)); + short_tempbuf = calloc(1, (SCREENHEIGHT * 4) * sizeof(*short_tempbuf)); + int_tempbuf = calloc(1, (SCREENHEIGHT * 4) * sizeof(*int_tempbuf)); + + temp_x = 0; +} + +// +// R_InitBuffer +// Creats lookup tables that avoid +// multiplies and other hazzles +// for getting the framebuffer address +// of a pixel to draw. +// + +void R_InitBuffer(int width, int height) +{ + int i=0; + // Handle resize, + // e.g. smaller view windows + // with border and/or status bar. + + viewwindowx = (SCREENWIDTH-width) >> 1; + + // Same with base row offset. + + viewwindowy = width==SCREENWIDTH ? 0 : (SCREENHEIGHT-ST_SCALED_HEIGHT-height)>>1; + + drawvars.byte_topleft = screens[0].data + viewwindowy*screens[0].byte_pitch + viewwindowx; + drawvars.short_topleft = (unsigned short *)(screens[0].data) + viewwindowy*screens[0].short_pitch + viewwindowx; + drawvars.int_topleft = (unsigned int *)(screens[0].data) + viewwindowy*screens[0].int_pitch + viewwindowx; + drawvars.byte_pitch = screens[0].byte_pitch; + drawvars.short_pitch = screens[0].short_pitch; + drawvars.int_pitch = screens[0].int_pitch; + + if (V_GetMode() == VID_MODE8) { + for (i=0; i= 10 : screenblocks == 10); + } + else +#endif + { + only_stbar = screenblocks >= 10; + } + + if (only_stbar && ST_SCALED_OFFSETX > 0) + { + int stbar_top = SCREENHEIGHT - ST_SCALED_HEIGHT; + + V_FillFlat(grnrock.lumpnum, 1, + 0, stbar_top, ST_SCALED_OFFSETX, ST_SCALED_HEIGHT, VPT_NONE); + V_FillFlat(grnrock.lumpnum, 1, + SCREENWIDTH - ST_SCALED_OFFSETX, stbar_top, ST_SCALED_OFFSETX, ST_SCALED_HEIGHT, VPT_NONE); + + // line between view and status bar + V_FillPatch(brdr_b.lumpnum, 1, 0, stbar_top, ST_SCALED_OFFSETX, brdr_b.height, VPT_NONE); + V_FillPatch(brdr_b.lumpnum, 1, SCREENWIDTH - ST_SCALED_OFFSETX, stbar_top, ST_SCALED_OFFSETX, brdr_b.height, VPT_NONE); + + return; + } + } + + if (scaledviewwidth == SCREENWIDTH) + return; + + V_FillFlat(grnrock.lumpnum, 1, 0, 0, SCREENWIDTH, SCREENHEIGHT, VPT_NONE); + + // line between view and status bar + if ((ratio_multiplier != ratio_scale || wide_offsety) && + (automap || scaledviewwidth == SCREENWIDTH)) + { + V_FillPatch(brdr_b.lumpnum, 1, 0, SCREENHEIGHT - ST_SCALED_HEIGHT, SCREENWIDTH, brdr_b.height, VPT_NONE); + } + + V_FillPatch(brdr_t.lumpnum, 1, viewwindowx, viewwindowy-8, scaledviewwidth, brdr_t.height, VPT_NONE); + + V_FillPatch(brdr_b.lumpnum, 1, viewwindowx, viewwindowy+viewheight, scaledviewwidth, brdr_b.height, VPT_NONE); + + V_FillPatch(brdr_l.lumpnum, 1, viewwindowx-8, viewwindowy, brdr_l.width, viewheight, VPT_NONE); + + V_FillPatch(brdr_r.lumpnum, 1, viewwindowx+scaledviewwidth, viewwindowy, brdr_r.width, viewheight, VPT_NONE); + + // Draw beveled edge. + V_DrawNumPatch(viewwindowx-8,viewwindowy-8,1,brdr_tl.lumpnum, CR_DEFAULT, VPT_NONE); + + V_DrawNumPatch(viewwindowx+scaledviewwidth,viewwindowy-8,1,brdr_tr.lumpnum, CR_DEFAULT, VPT_NONE); + + V_DrawNumPatch(viewwindowx-8,viewwindowy+viewheight,1,brdr_bl.lumpnum, CR_DEFAULT, VPT_NONE); + + V_DrawNumPatch(viewwindowx+scaledviewwidth,viewwindowy+viewheight,1,brdr_br.lumpnum, CR_DEFAULT, VPT_NONE); +} + +// +// Copy a screen buffer. +// + +void R_VideoErase(int x, int y, int count) +{ + if (V_GetMode() != VID_MODEGL) + memcpy(screens[0].data+y*screens[0].byte_pitch+x*V_GetPixelDepth(), + screens[1].data+y*screens[1].byte_pitch+x*V_GetPixelDepth(), + count*V_GetPixelDepth()); // LFB copy. +} + +// +// R_DrawViewBorder +// Draws the border around the view +// for different size windows? +// + +void R_DrawViewBorder(void) +{ + int top, side, i; + + if (V_GetMode() == VID_MODEGL) { + // proff 11/99: we don't have a backscreen in OpenGL from where we can copy this + R_FillBackScreen(); + return; + } + + // e6y: wide-res + if ((ratio_multiplier != ratio_scale || wide_offsety) && + ST_SCALED_OFFSETX > 0 && + ((SCREENHEIGHT != viewheight) || + ((automapmode & am_active) && ! (automapmode & am_overlay)))) + { + for (i = (SCREENHEIGHT - ST_SCALED_HEIGHT); i < SCREENHEIGHT; i++) + { + R_VideoErase (0, i, ST_SCALED_OFFSETX); + R_VideoErase (SCREENWIDTH - ST_SCALED_OFFSETX, i, ST_SCALED_OFFSETX); + } + } + + if ( viewheight >= ( SCREENHEIGHT - ST_SCALED_HEIGHT )) + return; // if high-res, don't go any further! + + top = ((SCREENHEIGHT-ST_SCALED_HEIGHT)-viewheight)/2; + side = (SCREENWIDTH-scaledviewwidth)/2; + + // copy top + for (i = 0; i < top; i++) + R_VideoErase (0, i, SCREENWIDTH); + + // copy sides + for (i = top; i < (top+viewheight); i++) { + R_VideoErase (0, i, side); + R_VideoErase (viewwidth+side, i, side); + } + + // copy bottom + for (i = top+viewheight; i < (SCREENHEIGHT - ST_SCALED_HEIGHT); i++) + R_VideoErase (0, i, SCREENWIDTH); +} diff --git a/src/r_draw.h b/src/r_draw.h new file mode 100644 index 0000000..b07c130 --- /dev/null +++ b/src/r_draw.h @@ -0,0 +1,175 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System specific interface stuff. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_DRAW__ +#define __R_DRAW__ + +#include "r_defs.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +enum column_pipeline_e { + RDC_PIPELINE_STANDARD, + RDC_PIPELINE_TRANSLUCENT, + RDC_PIPELINE_TRANSLATED, + RDC_PIPELINE_FUZZ, + RDC_PIPELINE_MAXPIPELINES, +}; + +// Used to specify what kind of filering you want +enum draw_filter_type_e { + RDRAW_FILTER_NONE, + RDRAW_FILTER_POINT, + RDRAW_FILTER_LINEAR, + RDRAW_FILTER_ROUNDED, + RDRAW_FILTER_MAXFILTERS +}; + +// Used to specify what kind of column edge rendering to use on masked +// columns. SQUARE = standard, SLOPED = slope the column edge up or down +// based on neighboring columns +enum sloped_edge_type_e { + RDRAW_MASKEDCOLUMNEDGE_SQUARE, + RDRAW_MASKEDCOLUMNEDGE_SLOPED +}; + +typedef enum +{ + DRAW_COLUMN_ISPATCH = 0x00000001 +} draw_column_flags_e; + +typedef struct draw_column_vars_s* pdraw_column_vars_s; +typedef void (*R_DrawColumn_f)(pdraw_column_vars_s dcvars); + +// Packaged into a struct - POPE +typedef struct draw_column_vars_s +{ + int x; + int yl; + int yh; + int dy; + fixed_t z; // the current column z coord + fixed_t iscale; + fixed_t texturemid; + int texheight; // killough + fixed_t texu; // the current column u coord + const byte *source; // first pixel in a column + const byte *prevsource; // first pixel in previous column + const byte *nextsource; // first pixel in next column + const lighttable_t *colormap; + const lighttable_t *nextcolormap; + const byte *translation; + int edgeslope; // OR'ed RDRAW_EDGESLOPE_* + // 1 if R_DrawColumn* is currently drawing a masked column, otherwise 0 + int drawingmasked; + enum sloped_edge_type_e edgetype; + unsigned int flags; //e6y: for detect patches ind colfunc() +} draw_column_vars_t; + +void R_SetDefaultDrawColumnVars(draw_column_vars_t *dcvars); + +void R_VideoErase(int x, int y, int count); + +typedef struct { + int y; + int x1; + int x2; + fixed_t z; // the current span z coord + fixed_t xfrac; + fixed_t yfrac; + fixed_t xstep; + fixed_t ystep; + const byte *source; // start of a 64*64 tile image + const lighttable_t *colormap; + const lighttable_t *nextcolormap; +} draw_span_vars_t; + +typedef struct { + byte *byte_topleft; + unsigned short *short_topleft; + unsigned int *int_topleft; + int byte_pitch; + int short_pitch; + int int_pitch; + + enum draw_filter_type_e filterwall; + enum draw_filter_type_e filterfloor; + enum draw_filter_type_e filtersprite; + enum draw_filter_type_e filterz; + enum draw_filter_type_e filterpatch; + + enum sloped_edge_type_e sprite_edges; + enum sloped_edge_type_e patch_edges; + + // Used to specify an early-out magnification threshold for filtering. + // If a texture is being minified (dcvars.iscale > rdraw_magThresh), then it + // drops back to point filtering. + fixed_t mag_threshold; +} draw_vars_t; + +extern draw_vars_t drawvars; + +extern byte playernumtotrans[MAXPLAYERS]; // CPhipps - what translation table for what player +extern byte *translationtables; + +R_DrawColumn_f R_GetDrawColumnFunc(enum column_pipeline_e type, + enum draw_filter_type_e filter, + enum draw_filter_type_e filterz); + +// Span blitting for rows, floor/ceiling. No Spectre effect needed. +typedef void (*R_DrawSpan_f)(draw_span_vars_t *dsvars); +R_DrawSpan_f R_GetDrawSpanFunc(enum draw_filter_type_e filter, + enum draw_filter_type_e filterz); +void R_DrawSpan(draw_span_vars_t *dsvars); + +void R_InitBuffer(int width, int height); + +void R_InitBuffersRes(void); + +// Initialize color translation tables, for player rendering etc. +void R_InitTranslationTables(void); + +// Rendering function. +void R_FillBackScreen(void); + +// If the view size is not full screen, draws a border around it. +void R_DrawViewBorder(void); + +// haleyjd 09/13/04: new function to call from main rendering loop +// which gets rid of the unnecessary reset of various variables during +// column drawing. +void R_ResetColumnBuffer(void); + +#endif diff --git a/src/r_drawcolpipeline.inl b/src/r_drawcolpipeline.inl new file mode 100644 index 0000000..8e122cd --- /dev/null +++ b/src/r_drawcolpipeline.inl @@ -0,0 +1,51 @@ + +// no color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_PointUV) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_NOCOLMAP) +#include "r_drawcolumn.inl" + +// simple depth color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_PointUV_PointZ) +#define R_DRAWCOLUMN_PIPELINE R_DRAWCOLUMN_PIPELINE_BASE +#include "r_drawcolumn.inl" + +// z-dither +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_PointUV_LinearZ) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_DITHERZ) +#include "r_drawcolumn.inl" + +// bilinear with no color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_LinearUV) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_BILINEAR | RDC_NOCOLMAP) +#include "r_drawcolumn.inl" + +// bilinear with simple depth color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_LinearUV_PointZ) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_BILINEAR) +#include "r_drawcolumn.inl" + +// bilinear + z-dither +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_LinearUV_LinearZ) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_BILINEAR | RDC_DITHERZ) +#include "r_drawcolumn.inl" + +// rounded with no color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_RoundedUV) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_ROUNDED | RDC_NOCOLMAP) +#include "r_drawcolumn.inl" + +// rounded with simple depth color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_RoundedUV_PointZ) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_ROUNDED) +#include "r_drawcolumn.inl" + +// rounded + z-dither +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_RoundedUV_LinearZ) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_ROUNDED | RDC_DITHERZ) +#include "r_drawcolumn.inl" + +#undef R_FLUSHWHOLE_FUNCNAME +#undef R_FLUSHHEADTAIL_FUNCNAME +#undef R_FLUSHQUAD_FUNCNAME +#undef R_DRAWCOLUMN_FUNCNAME_COMPOSITE +#undef R_DRAWCOLUMN_PIPELINE_BITS diff --git a/src/r_drawcolumn.inl b/src/r_drawcolumn.inl new file mode 100644 index 0000000..0bf64e3 --- /dev/null +++ b/src/r_drawcolumn.inl @@ -0,0 +1,381 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + + +#if (R_DRAWCOLUMN_PIPELINE_BITS == 8) +#define SCREENTYPE byte +#define TEMPBUF byte_tempbuf +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 15) +#define SCREENTYPE unsigned short +#define TEMPBUF short_tempbuf +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 16) +#define SCREENTYPE unsigned short +#define TEMPBUF short_tempbuf +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 32) +#define SCREENTYPE unsigned int +#define TEMPBUF int_tempbuf +#endif + +#define GETDESTCOLOR8(col) (col) +#define GETDESTCOLOR15(col) (col) +#define GETDESTCOLOR16(col) (col) +#define GETDESTCOLOR32(col) (col) + +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLATED) +#define GETCOL8_MAPPED(col) (translation[(col)]) +#else +#define GETCOL8_MAPPED(col) (col) +#endif + +#if (R_DRAWCOLUMN_PIPELINE & RDC_NOCOLMAP) + #define GETCOL8_DEPTH(col) GETCOL8_MAPPED(col) +#else + #if (R_DRAWCOLUMN_PIPELINE & RDC_DITHERZ) + #define GETCOL8_DEPTH(col) (dither_colormaps[filter_getDitheredPixelLevel(x, y, fracz)][GETCOL8_MAPPED(col)]) + #else + #define GETCOL8_DEPTH(col) colormap[GETCOL8_MAPPED(col)] + #endif +#endif + +#if (R_DRAWCOLUMN_PIPELINE & RDC_BILINEAR) + #define GETCOL8(frac, nextfrac) GETCOL8_DEPTH(filter_getDitheredForColumn(x,y,frac,nextfrac)) + #define GETCOL15(frac, nextfrac) filter_getFilteredForColumn15(GETCOL8_DEPTH,frac,nextfrac) + #define GETCOL16(frac, nextfrac) filter_getFilteredForColumn16(GETCOL8_DEPTH,frac,nextfrac) + #define GETCOL32(frac, nextfrac) filter_getFilteredForColumn32(GETCOL8_DEPTH,frac,nextfrac) +#elif (R_DRAWCOLUMN_PIPELINE & RDC_ROUNDED) + #define GETCOL8(frac, nextfrac) GETCOL8_DEPTH(filter_getRoundedForColumn(frac,nextfrac)) + #define GETCOL15(frac, nextfrac) VID_PAL15(GETCOL8_DEPTH(filter_getRoundedForColumn(frac,nextfrac)), VID_COLORWEIGHTMASK) + #define GETCOL16(frac, nextfrac) VID_PAL16(GETCOL8_DEPTH(filter_getRoundedForColumn(frac,nextfrac)), VID_COLORWEIGHTMASK) + #define GETCOL32(frac, nextfrac) VID_PAL32(GETCOL8_DEPTH(filter_getRoundedForColumn(frac,nextfrac)), VID_COLORWEIGHTMASK) +#else + #define GETCOL8(frac, nextfrac) GETCOL8_DEPTH(source[(frac)>>FRACBITS]) + #define GETCOL15(frac, nextfrac) VID_PAL15(GETCOL8_DEPTH(source[(frac)>>FRACBITS]), VID_COLORWEIGHTMASK) + #define GETCOL16(frac, nextfrac) VID_PAL16(GETCOL8_DEPTH(source[(frac)>>FRACBITS]), VID_COLORWEIGHTMASK) + #define GETCOL32(frac, nextfrac) VID_PAL32(GETCOL8_DEPTH(source[(frac)>>FRACBITS]), VID_COLORWEIGHTMASK) +#endif + +#if (R_DRAWCOLUMN_PIPELINE & (RDC_BILINEAR|RDC_ROUNDED|RDC_DITHERZ)) + #define INCY(y) (y++) +#else + #define INCY(y) +#endif + +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) +#define COLTYPE (COL_TRANS) +#elif (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) +#define COLTYPE (COL_FUZZ) +#else +#define COLTYPE (COL_OPAQUE) +#endif + +#if (R_DRAWCOLUMN_PIPELINE_BITS == 8) + #define GETCOL(frac, nextfrac) GETCOL8(frac, nextfrac) + #define GETDESTCOLOR(col) GETDESTCOLOR8(col) +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 15) + #define GETCOL(frac, nextfrac) GETCOL15(frac, nextfrac) + #define GETDESTCOLOR(col) GETDESTCOLOR15(col) +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 16) + #define GETCOL(frac, nextfrac) GETCOL16(frac, nextfrac) + #define GETDESTCOLOR(col) GETDESTCOLOR16(col) +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 32) + #define GETCOL(frac, nextfrac) GETCOL32(frac, nextfrac) + #define GETDESTCOLOR(col) GETDESTCOLOR32(col) +#endif + +static void R_DRAWCOLUMN_FUNCNAME(draw_column_vars_t *dcvars) +{ + int count; + SCREENTYPE *dest; // killough + fixed_t frac; + const fixed_t fracstep = dcvars->iscale; +#if ((R_DRAWCOLUMN_PIPELINE & RDC_BILINEAR) && (R_DRAWCOLUMN_PIPELINE_BITS != 8)) + const fixed_t slope_texu = (dcvars->source == dcvars->nextsource) ? 0 : dcvars->texu & 0xffff; +#else + const fixed_t slope_texu = dcvars->texu; +#endif + + // drop back to point filtering if we're minifying +#if (R_DRAWCOLUMN_PIPELINE & (RDC_BILINEAR|RDC_ROUNDED)) + if (dcvars->iscale > drawvars.mag_threshold) { + R_GetDrawColumnFunc(R_DRAWCOLUMN_PIPELINE_TYPE, + RDRAW_FILTER_POINT, + drawvars.filterz)(dcvars); + return; + } +#endif + +#if (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + // Adjust borders. Low... + if (!dcvars->yl) + dcvars->yl = 1; + + // .. and high. + if (dcvars->yh == viewheight-1) + dcvars->yh = viewheight - 2; +#endif + + // leban 1/17/99: + // removed the + 1 here, adjusted the if test, and added an increment + // later. this helps a compiler pipeline a bit better. the x86 + // assembler also does this. + + count = dcvars->yh - dcvars->yl; + + // leban 1/17/99: + // this case isn't executed too often. depending on how many instructions + // there are between here and the second if test below, this case could + // be moved down and might save instructions overall. since there are + // probably different wads that favor one way or the other, i'll leave + // this alone for now. + if (count < 0) // Zero length, column does not exceed a pixel. + return; + +#ifdef RANGECHECK + if (dcvars->x >= SCREENWIDTH + || dcvars->yl < 0 + || dcvars->yh >= SCREENHEIGHT) + I_Error("R_DrawColumn: %i to %i at %i", dcvars->yl, dcvars->yh, dcvars->x); +#endif + + // Determine scaling, which is the only mapping to be done. + #if (R_DRAWCOLUMN_PIPELINE & RDC_BILINEAR) + frac = dcvars->texturemid - (FRACUNIT>>1) + (dcvars->yl-centery)*fracstep; + #else + if (dcvars->flags & DRAW_COLUMN_ISPATCH) + frac = ((dcvars->yl - dcvars->dy) * fracstep) & 0xFFFF; + else + frac = dcvars->texturemid + (dcvars->yl-centery)*fracstep; + #endif + + if (dcvars->drawingmasked && dcvars->edgetype == RDRAW_MASKEDCOLUMNEDGE_SLOPED) { + // slope the top and bottom column edge based on the fractional u coordinate + // and dcvars->edgeslope, which were set in R_DrawMaskedColumn + // in r_things.c + if (dcvars->yl != 0) { + if (dcvars->edgeslope & RDRAW_EDGESLOPE_TOP_UP) { + // [/#] + int shift = ((0xffff-(slope_texu & 0xffff))/dcvars->iscale); + dcvars->yl += shift; + count -= shift; + frac += 0xffff-(slope_texu & 0xffff); + } + else if (dcvars->edgeslope & RDRAW_EDGESLOPE_TOP_DOWN) { + // [#\] + int shift = ((slope_texu & 0xffff)/dcvars->iscale); + dcvars->yl += shift; + count -= shift; + frac += slope_texu & 0xffff; + } + } + if (dcvars->yh != viewheight-1) { + if (dcvars->edgeslope & RDRAW_EDGESLOPE_BOT_UP) { + // [#/] + int shift = ((0xffff-(slope_texu & 0xffff))/dcvars->iscale); + dcvars->yh -= shift; + count -= shift; + } + else if (dcvars->edgeslope & RDRAW_EDGESLOPE_BOT_DOWN) { + // [\#] + int shift = ((slope_texu & 0xffff)/dcvars->iscale); + dcvars->yh -= shift; + count -= shift; + } + } + if (count <= 0) return; + } + + // Framebuffer destination address. + // SoM: MAGIC + { + // haleyjd: reordered predicates + if(temp_x == 4 || + (temp_x && (temptype != COLTYPE || temp_x + startx != dcvars->x))) + R_FlushColumns(); + + if(!temp_x) + { + startx = dcvars->x; + tempyl[0] = commontop = dcvars->yl; + tempyh[0] = commonbot = dcvars->yh; + temptype = COLTYPE; +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) + temptranmap = tranmap; +#elif (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + tempfuzzmap = fullcolormap; // SoM 7-28-04: Fix the fuzz problem. +#endif + R_FlushWholeColumns = R_FLUSHWHOLE_FUNCNAME; + R_FlushHTColumns = R_FLUSHHEADTAIL_FUNCNAME; + R_FlushQuadColumn = R_FLUSHQUAD_FUNCNAME; + dest = &TEMPBUF[dcvars->yl << 2]; + } else { + tempyl[temp_x] = dcvars->yl; + tempyh[temp_x] = dcvars->yh; + + if(dcvars->yl > commontop) + commontop = dcvars->yl; + if(dcvars->yh < commonbot) + commonbot = dcvars->yh; + + dest = &TEMPBUF[(dcvars->yl << 2) + temp_x]; + } + temp_x += 1; + } + +// do nothing else when drawin fuzz columns +#if (!(R_DRAWCOLUMN_PIPELINE & RDC_FUZZ)) + { + const byte *source = dcvars->source; + const lighttable_t *colormap = dcvars->colormap; + const byte *translation = dcvars->translation; +#if (R_DRAWCOLUMN_PIPELINE & (RDC_BILINEAR|RDC_ROUNDED|RDC_DITHERZ)) + int y = dcvars->yl; + const int x = dcvars->x; +#endif +#if (R_DRAWCOLUMN_PIPELINE & RDC_DITHERZ) + const int fracz = (dcvars->z >> 6) & 255; + const byte *dither_colormaps[2] = { dcvars->colormap, dcvars->nextcolormap }; +#endif +#if (R_DRAWCOLUMN_PIPELINE & RDC_BILINEAR) + #if (R_DRAWCOLUMN_PIPELINE_BITS == 8) + const int yl = dcvars->yl; + const byte *dither_sources[2] = { dcvars->source, dcvars->nextsource }; + const unsigned int filter_fracu = (dcvars->source == dcvars->nextsource) ? 0 : (dcvars->texu>>8) & 0xff; + #else + const byte *nextsource = dcvars->nextsource; + const unsigned int filter_fracu = (dcvars->source == dcvars->nextsource) ? 0 : dcvars->texu & 0xffff; + #endif +#endif +#if (R_DRAWCOLUMN_PIPELINE & RDC_ROUNDED) + const byte *prevsource = dcvars->prevsource; + const byte *nextsource = dcvars->nextsource; + const unsigned int filter_fracu = (dcvars->source == dcvars->nextsource) ? 0 : (dcvars->texu>>8) & 0xff; +#endif + + count++; + + // Inner loop that does the actual texture mapping, + // e.g. a DDA-lile scaling. + // This is as fast as it gets. (Yeah, right!!! -- killough) + // + // killough 2/1/98: more performance tuning + + if (dcvars->texheight == 128) { + #define FIXEDT_128MASK ((127<texheight == 0) { + /* cph - another special case */ + while (count--) { + *dest = GETDESTCOLOR(GETCOL(frac, (frac+FRACUNIT))); + INCY(y); + dest += 4; + frac += fracstep; + } + } else { + unsigned heightmask = dcvars->texheight-1; // CPhipps - specify type + if (! (dcvars->texheight & heightmask) ) { // power of 2 -- killough + fixed_t fixedt_heightmask = (heightmask<=0) { // texture height is a power of 2 -- killough + *dest = GETDESTCOLOR(GETCOL(frac & fixedt_heightmask, (frac+FRACUNIT) & fixedt_heightmask)); + INCY(y); + dest += 4; + frac += fracstep; + *dest = GETDESTCOLOR(GETCOL(frac & fixedt_heightmask, (frac+FRACUNIT) & fixedt_heightmask)); + INCY(y); + dest += 4; + frac += fracstep; + } + if (count & 1) + *dest = GETDESTCOLOR(GETCOL(frac & fixedt_heightmask, (frac+FRACUNIT) & fixedt_heightmask)); + INCY(y); + } else { + fixed_t nextfrac = 0; + + heightmask++; + heightmask <<= FRACBITS; + + if (frac < 0) + while ((frac += heightmask) < 0); + else + while (frac >= (int)heightmask) + frac -= heightmask; + +#if (R_DRAWCOLUMN_PIPELINE & (RDC_BILINEAR|RDC_ROUNDED)) + nextfrac = frac + FRACUNIT; + while (nextfrac >= (int)heightmask) + nextfrac -= heightmask; +#endif + +#define INCFRAC(f) if ((f += fracstep) >= (int)heightmask) f -= heightmask; + + while (count--) { + // Re-map color indices from wall texture column + // using a lighting/special effects LUT. + + // heightmask is the Tutti-Frutti fix -- killough + + *dest = GETDESTCOLOR(GETCOL(frac, nextfrac)); + INCY(y); + dest += 4; + INCFRAC(frac); +#if (R_DRAWCOLUMN_PIPELINE & (RDC_BILINEAR|RDC_ROUNDED)) + INCFRAC(nextfrac); +#endif + } + } + } + } +#endif // (!(R_DRAWCOLUMN_PIPELINE & RDC_FUZZ)) +} + +#undef GETDESTCOLOR32 +#undef GETDESTCOLOR16 +#undef GETDESTCOLOR15 +#undef GETDESTCOLOR8 +#undef GETDESTCOLOR +#undef GETCOL8_MAPPED +#undef GETCOL8_DEPTH +#undef GETCOL32 +#undef GETCOL16 +#undef GETCOL15 +#undef GETCOL8 +#undef GETCOL +#undef INCY +#undef INCFRAC +#undef COLTYPE +#undef TEMPBUF +#undef SCREENTYPE + +#undef R_DRAWCOLUMN_FUNCNAME +#undef R_DRAWCOLUMN_PIPELINE diff --git a/src/r_drawflush.inl b/src/r_drawflush.inl new file mode 100644 index 0000000..4f30f60 --- /dev/null +++ b/src/r_drawflush.inl @@ -0,0 +1,300 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + +#if (R_DRAWCOLUMN_PIPELINE_BITS == 8) +#define SCREENTYPE byte +#define TOPLEFT byte_topleft +#define PITCH byte_pitch +#define TEMPBUF byte_tempbuf +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 15) +#define SCREENTYPE unsigned short +#define TOPLEFT short_topleft +#define PITCH short_pitch +#define TEMPBUF short_tempbuf +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 16) +#define SCREENTYPE unsigned short +#define TOPLEFT short_topleft +#define PITCH short_pitch +#define TEMPBUF short_tempbuf +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 32) +#define SCREENTYPE unsigned int +#define TOPLEFT int_topleft +#define PITCH int_pitch +#define TEMPBUF int_tempbuf +#endif + +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) +#define GETDESTCOLOR8(col1, col2) (temptranmap[((col1)<<8)+(col2)]) +#define GETDESTCOLOR15(col1, col2) (GETBLENDED15_3268((col1), (col2))) +#define GETDESTCOLOR16(col1, col2) (GETBLENDED16_3268((col1), (col2))) +#define GETDESTCOLOR32(col1, col2) (GETBLENDED32_3268((col1), (col2))) +#elif (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) +#define GETDESTCOLOR8(col) (tempfuzzmap[6*256+(col)]) +#define GETDESTCOLOR15(col) GETBLENDED15_9406(col, 0) +#define GETDESTCOLOR16(col) GETBLENDED16_9406(col, 0) +#define GETDESTCOLOR32(col) GETBLENDED32_9406(col, 0) +#else +#define GETDESTCOLOR8(col) (col) +#define GETDESTCOLOR15(col) (col) +#define GETDESTCOLOR16(col) (col) +#define GETDESTCOLOR32(col) (col) +#endif + +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) + #if (R_DRAWCOLUMN_PIPELINE_BITS == 8) + #define GETDESTCOLOR(col1, col2) GETDESTCOLOR8(col1, col2) + #elif (R_DRAWCOLUMN_PIPELINE_BITS == 15) + #define GETDESTCOLOR(col1, col2) GETDESTCOLOR15(col1, col2) + #elif (R_DRAWCOLUMN_PIPELINE_BITS == 16) + #define GETDESTCOLOR(col1, col2) GETDESTCOLOR16(col1, col2) + #elif (R_DRAWCOLUMN_PIPELINE_BITS == 32) + #define GETDESTCOLOR(col1, col2) GETDESTCOLOR32(col1, col2) + #endif +#else + #if (R_DRAWCOLUMN_PIPELINE_BITS == 8) + #define GETDESTCOLOR(col) GETDESTCOLOR8(col) + #elif (R_DRAWCOLUMN_PIPELINE_BITS == 15) + #define GETDESTCOLOR(col) GETDESTCOLOR15(col) + #elif (R_DRAWCOLUMN_PIPELINE_BITS == 16) + #define GETDESTCOLOR(col) GETDESTCOLOR16(col) + #elif (R_DRAWCOLUMN_PIPELINE_BITS == 32) + #define GETDESTCOLOR(col) GETDESTCOLOR32(col) + #endif +#endif + +// +// R_FlushWholeOpaque +// +// Flushes the entire columns in the buffer, one at a time. +// This is used when a quad flush isn't possible. +// Opaque version -- no remapping whatsoever. +// +static void R_FLUSHWHOLE_FUNCNAME(void) +{ + SCREENTYPE *source; + SCREENTYPE *dest; + int count, yl; + + while(--temp_x >= 0) + { + yl = tempyl[temp_x]; + source = &TEMPBUF[temp_x + (yl << 2)]; + dest = drawvars.TOPLEFT + yl*drawvars.PITCH + startx + temp_x; + count = tempyh[temp_x] - yl + 1; + + while(--count >= 0) + { +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) + *dest = GETDESTCOLOR(*dest, *source); +#elif (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + // SoM 7-28-04: Fix the fuzz problem. + *dest = GETDESTCOLOR(dest[fuzzoffset[fuzzpos]]); + + // Clamp table lookup index. + if(++fuzzpos == FUZZTABLE) + fuzzpos = 0; +#else + *dest = *source; +#endif + + source += 4; + dest += drawvars.PITCH; + } + } +} + +// +// R_FlushHTOpaque +// +// Flushes the head and tail of columns in the buffer in +// preparation for a quad flush. +// Opaque version -- no remapping whatsoever. +// +static void R_FLUSHHEADTAIL_FUNCNAME(void) +{ + SCREENTYPE *source; + SCREENTYPE *dest; + int count, colnum = 0; + int yl, yh; + + while(colnum < 4) + { + yl = tempyl[colnum]; + yh = tempyh[colnum]; + + // flush column head + if(yl < commontop) + { + source = &TEMPBUF[colnum + (yl << 2)]; + dest = drawvars.TOPLEFT + yl*drawvars.PITCH + startx + colnum; + count = commontop - yl; + + while(--count >= 0) + { +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) + // haleyjd 09/11/04: use temptranmap here + *dest = GETDESTCOLOR(*dest, *source); +#elif (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + // SoM 7-28-04: Fix the fuzz problem. + *dest = GETDESTCOLOR(dest[fuzzoffset[fuzzpos]]); + + // Clamp table lookup index. + if(++fuzzpos == FUZZTABLE) + fuzzpos = 0; +#else + *dest = *source; +#endif + + source += 4; + dest += drawvars.PITCH; + } + } + + // flush column tail + if(yh > commonbot) + { + source = &TEMPBUF[colnum + ((commonbot + 1) << 2)]; + dest = drawvars.TOPLEFT + (commonbot + 1)*drawvars.PITCH + startx + colnum; + count = yh - commonbot; + + while(--count >= 0) + { +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) + // haleyjd 09/11/04: use temptranmap here + *dest = GETDESTCOLOR(*dest, *source); +#elif (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + // SoM 7-28-04: Fix the fuzz problem. + *dest = GETDESTCOLOR(dest[fuzzoffset[fuzzpos]]); + + // Clamp table lookup index. + if(++fuzzpos == FUZZTABLE) + fuzzpos = 0; +#else + *dest = *source; +#endif + + source += 4; + dest += drawvars.PITCH; + } + } + ++colnum; + } +} + +static void R_FLUSHQUAD_FUNCNAME(void) +{ + SCREENTYPE *source = &TEMPBUF[commontop << 2]; + SCREENTYPE *dest = drawvars.TOPLEFT + commontop*drawvars.PITCH + startx; + int count; +#if (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + int fuzz1, fuzz2, fuzz3, fuzz4; + + fuzz1 = fuzzpos; + fuzz2 = (fuzz1 + tempyl[1]) % FUZZTABLE; + fuzz3 = (fuzz2 + tempyl[2]) % FUZZTABLE; + fuzz4 = (fuzz3 + tempyl[3]) % FUZZTABLE; +#endif + + count = commonbot - commontop + 1; + +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) + while(--count >= 0) + { + dest[0] = GETDESTCOLOR(dest[0], source[0]); + dest[1] = GETDESTCOLOR(dest[1], source[1]); + dest[2] = GETDESTCOLOR(dest[2], source[2]); + dest[3] = GETDESTCOLOR(dest[3], source[3]); + source += 4 * sizeof(byte); + dest += drawvars.PITCH * sizeof(byte); + } +#elif (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + while(--count >= 0) + { + dest[0] = GETDESTCOLOR(dest[0 + fuzzoffset[fuzz1]]); + dest[1] = GETDESTCOLOR(dest[1 + fuzzoffset[fuzz2]]); + dest[2] = GETDESTCOLOR(dest[2 + fuzzoffset[fuzz3]]); + dest[3] = GETDESTCOLOR(dest[3 + fuzzoffset[fuzz4]]); + fuzz1 = (fuzz1 + 1) % FUZZTABLE; + fuzz2 = (fuzz2 + 1) % FUZZTABLE; + fuzz3 = (fuzz3 + 1) % FUZZTABLE; + fuzz4 = (fuzz4 + 1) % FUZZTABLE; + source += 4 * sizeof(byte); + dest += drawvars.PITCH * sizeof(byte); + } +#else + #if (R_DRAWCOLUMN_PIPELINE_BITS == 8) + if ((sizeof(int) == 4) && (((intptr_t)source % 4) == 0) && (((intptr_t)dest % 4) == 0)) { + while(--count >= 0) + { + *(int *)dest = *(int *)source; + source += 4 * sizeof(byte); + dest += drawvars.PITCH * sizeof(byte); + } + } else { + while(--count >= 0) + { + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; + dest[3] = source[3]; + source += 4 * sizeof(byte); + dest += drawvars.PITCH * sizeof(byte); + } + } + #else + while(--count >= 0) + { + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; + dest[3] = source[3]; + source += 4; + dest += drawvars.PITCH; + } + #endif +#endif +} + +#undef GETDESTCOLOR32 +#undef GETDESTCOLOR16 +#undef GETDESTCOLOR15 +#undef GETDESTCOLOR8 +#undef GETDESTCOLOR + +#undef TEMPBUF +#undef PITCH +#undef TOPLEFT +#undef SCREENTYPE + +#undef R_DRAWCOLUMN_PIPELINE_BITS +#undef R_DRAWCOLUMN_PIPELINE +#undef R_FLUSHWHOLE_FUNCNAME +#undef R_FLUSHHEADTAIL_FUNCNAME +#undef R_FLUSHQUAD_FUNCNAME diff --git a/src/r_drawspan.inl b/src/r_drawspan.inl new file mode 100644 index 0000000..84cc95d --- /dev/null +++ b/src/r_drawspan.inl @@ -0,0 +1,160 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + +// +// R_DrawSpan +// + +#if (R_DRAWSPAN_PIPELINE_BITS == 8) +#define SCREENTYPE byte +#define TOPLEFT byte_topleft +#define PITCH byte_pitch +#elif (R_DRAWSPAN_PIPELINE_BITS == 15) +#define SCREENTYPE unsigned short +#define TOPLEFT short_topleft +#define PITCH short_pitch +#elif (R_DRAWSPAN_PIPELINE_BITS == 16) +#define SCREENTYPE unsigned short +#define TOPLEFT short_topleft +#define PITCH short_pitch +#elif (R_DRAWSPAN_PIPELINE_BITS == 32) +#define SCREENTYPE unsigned int +#define TOPLEFT int_topleft +#define PITCH int_pitch +#endif + +#if (R_DRAWSPAN_PIPELINE & RDC_DITHERZ) + #define GETDEPTHMAP(col) dither_colormaps[filter_getDitheredPixelLevel(x1, y, fracz)][(col)] +#else + #define GETDEPTHMAP(col) colormap[(col)] +#endif + +#if (R_DRAWSPAN_PIPELINE_BITS == 8) + #define GETCOL_POINT(col) GETDEPTHMAP(col) + #define GETCOL_LINEAR(col) GETDEPTHMAP(col) +#elif (R_DRAWSPAN_PIPELINE_BITS == 15) + #define GETCOL_POINT(col) VID_PAL15(GETDEPTHMAP(col), VID_COLORWEIGHTMASK) + #define GETCOL_LINEAR(col) filter_getFilteredForSpan15(GETDEPTHMAP, xfrac, yfrac) +#elif (R_DRAWSPAN_PIPELINE_BITS == 16) + #define GETCOL_POINT(col) VID_PAL16(GETDEPTHMAP(col), VID_COLORWEIGHTMASK) + #define GETCOL_LINEAR(col) filter_getFilteredForSpan16(GETDEPTHMAP, xfrac, yfrac) +#elif (R_DRAWSPAN_PIPELINE_BITS == 32) + #define GETCOL_POINT(col) VID_PAL32(GETDEPTHMAP(col), VID_COLORWEIGHTMASK) + #define GETCOL_LINEAR(col) filter_getFilteredForSpan32(GETDEPTHMAP, xfrac, yfrac) +#endif + +#if (R_DRAWSPAN_PIPELINE & RDC_BILINEAR) + #define GETCOL(col) GETCOL_LINEAR(col) +#else + #define GETCOL(col) GETCOL_POINT(col) +#endif + +static void R_DRAWSPAN_FUNCNAME(draw_span_vars_t *dsvars) +{ +#if (R_DRAWSPAN_PIPELINE & (RDC_ROUNDED|RDC_BILINEAR)) + // drop back to point filtering if we're minifying + // 49152 = FRACUNIT * 0.75 + if ((D_abs(dsvars->xstep) > drawvars.mag_threshold) + || (D_abs(dsvars->ystep) > drawvars.mag_threshold)) + { + R_GetDrawSpanFunc(RDRAW_FILTER_POINT, + drawvars.filterz)(dsvars); + return; + } +#endif + { + unsigned count = dsvars->x2 - dsvars->x1 + 1; + fixed_t xfrac = dsvars->xfrac; + fixed_t yfrac = dsvars->yfrac; + const fixed_t xstep = dsvars->xstep; + const fixed_t ystep = dsvars->ystep; + const byte *source = dsvars->source; + const byte *colormap = dsvars->colormap; + SCREENTYPE *dest = drawvars.TOPLEFT + dsvars->y*drawvars.PITCH + dsvars->x1; +#if (R_DRAWSPAN_PIPELINE & (RDC_DITHERZ|RDC_BILINEAR)) + const int y = dsvars->y; + int x1 = dsvars->x1; +#endif +#if (R_DRAWSPAN_PIPELINE & RDC_DITHERZ) + const int fracz = (dsvars->z >> 12) & 255; + const byte *dither_colormaps[2] = { dsvars->colormap, dsvars->nextcolormap }; +#endif + + while (count) { +#if ((R_DRAWSPAN_PIPELINE_BITS != 8) && (R_DRAWSPAN_PIPELINE & RDC_BILINEAR)) + // truecolor bilinear filtered + *dest++ = GETCOL(0); + xfrac += xstep; + yfrac += ystep; + count--; + #if (R_DRAWSPAN_PIPELINE & RDC_DITHERZ) + x1--; + #endif +#elif (R_DRAWSPAN_PIPELINE & RDC_ROUNDED) + *dest++ = GETCOL(filter_getRoundedForSpan(xfrac, yfrac)); + xfrac += xstep; + yfrac += ystep; + count--; + #if (R_DRAWSPAN_PIPELINE & RDC_DITHERZ) + x1--; + #endif +#else + #if (R_DRAWSPAN_PIPELINE & RDC_BILINEAR) + // 8 bit bilinear + const fixed_t xtemp = ((xfrac >> 16) + (filter_getDitheredPixelLevel(x1, y, ((xfrac>>8)&0xff)))) & 63; + const fixed_t ytemp = ((yfrac >> 10) + 64*(filter_getDitheredPixelLevel(x1, y, ((yfrac>>8)&0xff)))) & 4032; + #else + const fixed_t xtemp = (xfrac >> 16) & 63; + const fixed_t ytemp = (yfrac >> 10) & 4032; + #endif + const fixed_t spot = xtemp | ytemp; + xfrac += xstep; + yfrac += ystep; + *dest++ = GETCOL(source[spot]); + count--; + #if (R_DRAWSPAN_PIPELINE & (RDC_DITHERZ|RDC_BILINEAR)) + x1--; + #endif +#endif + } + } +} + +#undef GETDEPTHMAP +#undef GETCOL_LINEAR +#undef GETCOL_POINT +#undef GETCOL +#undef PITCH +#undef TOPLEFT +#undef SCREENTYPE + +#undef R_DRAWSPAN_PIPELINE_BITS +#undef R_DRAWSPAN_PIPELINE +#undef R_DRAWSPAN_FUNCNAME diff --git a/src/r_filter.c b/src/r_filter.c new file mode 100644 index 0000000..2c393fe --- /dev/null +++ b/src/r_filter.c @@ -0,0 +1,121 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + +#include "doomtype.h" +#include "r_filter.h" + +#define DMR 16 +byte filter_ditherMatrix[DITHER_DIM][DITHER_DIM] = { + { 0*DMR, 14*DMR, 3*DMR, 13*DMR}, {11*DMR, 5*DMR, 8*DMR, 6*DMR}, + {12*DMR, 2*DMR, 15*DMR, 1*DMR}, { 7*DMR, 9*DMR, 4*DMR, 10*DMR} +}; + +byte filter_roundedUVMap[FILTER_UVDIM*FILTER_UVDIM]; +byte filter_roundedRowMap[4*16]; + +void R_FilterInit(void) { + int i,j,s,t; + + // scale2x takes the following source: + // A B C + // D E F + // G H I + // + // and doubles the size of E to produce: + // E0 E1 + // E2 E3 + // + // E0 = D == B && B != F && D != H ? D : E; + // E1 = B == F && B != D && F != H ? F : E; + // E2 = D == H && D != B && H != F ? D : E; + // E3 = H == F && D != H && B != F ? F : E; + // + // to make this comparison regimen faster, we encode source color + // equivalency into a single byte with the getCode() macro + // + // #define getCode(b,f,h,d) ( (b == f)<<0 | (f == h)<<1 | (h == d)<<2 | (d == b)<<3 ) + + // encode the scale2x conditionals into a lookup code + for (i=0; i<16; i++) { + // E0 = D == B && B != F && D != H ? D : E; // 10-0 => 1000 or 1010 => 8 or A + filter_roundedRowMap[0*16+i] = (i == 0x8 || i == 0xA) ? 0 : 1; + // E1 = B == F && B != D && F != H ? F : E; // 0-01 => 0101 or 0001 => 5 or 1 + filter_roundedRowMap[1*16+i] = (i == 0x5 || i == 0x1) ? 2 : 1; + // E2 = D == H && D != B && H != F ? D : E; // 010- => 0101 or 0100 => 5 or 4 + filter_roundedRowMap[2*16+i] = (i == 0x4 || i == 0x5) ? 0 : 1; + // E3 = H == F && D != H && B != F ? F : E; // -010 => 1010 or 0010 => A or 2 + filter_roundedRowMap[3*16+i] = (i == 0xA || i == 0x2) ? 2 : 1; + } + + /* + // fill the uvMap. this will return: + // 0/\1 + // /4 \ + // \ / + // 2\/3 + // .. based on the uv coordinates + */ + for (i=0; i=0 && t>=0) filter_roundedUVMap[i*FILTER_UVDIM+j] = (s+t > FILTER_UVDIM/2) ? 0 : 4; + else if (s>=0 && t<=0) filter_roundedUVMap[i*FILTER_UVDIM+j] = (s-t > FILTER_UVDIM/2) ? 2 : 4; + else if (s<=0 && t>=0) filter_roundedUVMap[i*FILTER_UVDIM+j] = (-s+t > FILTER_UVDIM/2) ? 1 : 4; + else if (s<=0 && t<=0) filter_roundedUVMap[i*FILTER_UVDIM+j] = (-s-t > FILTER_UVDIM/2) ? 3 : 4; + else filter_roundedUVMap[i*FILTER_UVDIM+j] = 4; + } + } +} + +byte *filter_getScale2xQuadColors(byte e, byte b, byte f, byte h, byte d) { + // A B C + // D E F + // G H I + // perform the Scale2x algorithm (quickly) to get the new quad to represent E + static byte quad[5]; + static byte rowColors[3]; + int code; + + rowColors[0] = d; + rowColors[1] = e; + rowColors[2] = f; + + #define getCode(b,f,h,d) ( (b == f)<<0 | (f == h)<<1 | (h == d)<<2 | (d == b)<<3 ) + + code = getCode(b,f,h,d); + quad[0] = rowColors[filter_roundedRowMap[0*16+code]]; + quad[1] = rowColors[filter_roundedRowMap[1*16+code]]; + quad[2] = rowColors[filter_roundedRowMap[2*16+code]]; + quad[3] = rowColors[filter_roundedRowMap[3*16+code]]; + quad[4] = e; + + return quad; +} diff --git a/src/r_filter.h b/src/r_filter.h new file mode 100644 index 0000000..8151eac --- /dev/null +++ b/src/r_filter.h @@ -0,0 +1,174 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + +#ifndef R_FILTER_H +#define R_FILTER_H + +#define DITHER_DIM 4 + +extern byte filter_ditherMatrix[DITHER_DIM][DITHER_DIM]; +#define FILTER_UVBITS 6 +#define FILTER_UVDIM (1<> 8), which was empirically +// derived. the "-dcvars.yl" is apparently required to offset some minor +// shaking in coordinate y-axis and prevents dithering seams +#define FILTER_GETV(x,y,texV,nextRowTexV) \ + (filter_getDitheredPixelLevel(x, y, (((texV) - yl) >> 8)&0xff) ? ((nextRowTexV)>>FRACBITS) : ((texV)>>FRACBITS)) + +// Choose current column or next column to the right based on dither of the +// fractional texture U coord +#define filter_getDitheredForColumn(x, y, texV, nextRowTexV) \ + dither_sources[(filter_getDitheredPixelLevel(x, y, filter_fracu))][FILTER_GETV(x,y,texV,nextRowTexV)] + +#define filter_getRoundedForColumn(texV, nextRowTexV) \ + filter_getScale2xQuadColors( \ + source[ ((texV)>>FRACBITS) ], \ + source[ (MAX(0, ((texV)>>FRACBITS)-1)) ], \ + nextsource[ ((texV)>>FRACBITS) ], \ + source[ ((nextRowTexV)>>FRACBITS) ], \ + prevsource[ ((texV)>>FRACBITS) ] \ + ) \ + [ filter_roundedUVMap[ \ + ((filter_fracu>>(8-FILTER_UVBITS))<>8) & 0xff)>>(8-FILTER_UVBITS)) \ + ] ] + +#define filter_getRoundedForSpan(texU, texV) \ + filter_getScale2xQuadColors( \ + source[ (((texU)>>16)&0x3f) | (((texV)>>10)&0xfc0) ], \ + source[ (((texU)>>16)&0x3f) | ((((texV)-FRACUNIT)>>10)&0xfc0) ], \ + source[ ((((texU)+FRACUNIT)>>16)&0x3f) | (((texV)>>10)&0xfc0) ], \ + source[ (((texU)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0) ], \ + source[ ((((texU)-FRACUNIT)>>16)&0x3f) | (((texV)>>10)&0xfc0) ] \ + ) \ + [ filter_roundedUVMap[ \ + (((((texU)>>8) & 0xff)>>(8-FILTER_UVBITS))<>8) & 0xff)>>(8-FILTER_UVBITS)) \ + ] ] + +byte *filter_getScale2xQuadColors(byte e, byte b, byte f, byte h, byte d); + +// This is the horrendous macro version of the function commented out of +// r_filter.c. It does a bilinear blend on the four source texels for a +// given u and v +#define filter_getFilteredForColumn32(depthmap, texV, nextRowTexV) ( \ + VID_PAL32( depthmap(nextsource[(nextRowTexV)>>FRACBITS]), (filter_fracu*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL32( depthmap(source[(nextRowTexV)>>FRACBITS]), ((0xffff-filter_fracu)*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL32( depthmap(source[(texV)>>FRACBITS]), ((0xffff-filter_fracu)*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL32( depthmap(nextsource[(texV)>>FRACBITS]), (filter_fracu*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS) )) + +// The 16 bit method of the filtering doesn't really maintain enough +// accuracy for discerning viewers, but the alternative requires converting +// from 32 bit, which is slow and requires both the intPalette and the +// shortPalette to be in memory at the same time. +#define filter_getFilteredForColumn16(depthmap, texV, nextRowTexV) ( \ + VID_PAL16( depthmap(nextsource[(nextRowTexV)>>FRACBITS]), (filter_fracu*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL16( depthmap(source[(nextRowTexV)>>FRACBITS]), ((0xffff-filter_fracu)*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL16( depthmap(source[(texV)>>FRACBITS]), ((0xffff-filter_fracu)*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL16( depthmap(nextsource[(texV)>>FRACBITS]), (filter_fracu*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS) )) + +#define filter_getFilteredForColumn15(depthmap, texV, nextRowTexV) ( \ + VID_PAL15( depthmap(nextsource[(nextRowTexV)>>FRACBITS]), (filter_fracu*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL15( depthmap(source[(nextRowTexV)>>FRACBITS]), ((0xffff-filter_fracu)*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL15( depthmap(source[(texV)>>FRACBITS]), ((0xffff-filter_fracu)*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL15( depthmap(nextsource[(texV)>>FRACBITS]), (filter_fracu*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS) )) + +// Same as for column but wrapping at 64 +#define filter_getFilteredForSpan32(depthmap, texU, texV) ( \ + VID_PAL32( depthmap(source[ ((((texU)+FRACUNIT)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0)]), (unsigned int)(((texU)&0xffff)*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL32( depthmap(source[ (((texU)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0)]), (unsigned int)((0xffff-((texU)&0xffff))*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL32( depthmap(source[ (((texU)>>16)&0x3f) | (((texV)>>10)&0xfc0)]), (unsigned int)((0xffff-((texU)&0xffff))*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL32( depthmap(source[ ((((texU)+FRACUNIT)>>16)&0x3f) | (((texV)>>10)&0xfc0)]), (unsigned int)(((texU)&0xffff)*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS))) + +// Use 16 bit addition here since it's a little faster and the defects from +// such low-accuracy blending are less visible on spans +#define filter_getFilteredForSpan16(depthmap, texU, texV) ( \ + VID_PAL16( depthmap(source[ ((((texU)+FRACUNIT)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0)]), (unsigned int)(((texU)&0xffff)*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL16( depthmap(source[ (((texU)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0)]), (unsigned int)((0xffff-((texU)&0xffff))*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL16( depthmap(source[ (((texU)>>16)&0x3f) | (((texV)>>10)&0xfc0)]), (unsigned int)((0xffff-((texU)&0xffff))*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL16( depthmap(source[ ((((texU)+FRACUNIT)>>16)&0x3f) | (((texV)>>10)&0xfc0)]), (unsigned int)(((texU)&0xffff)*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS))) + +#define filter_getFilteredForSpan15(depthmap, texU, texV) ( \ + VID_PAL15( depthmap(source[ ((((texU)+FRACUNIT)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0)]), (unsigned int)(((texU)&0xffff)*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL15( depthmap(source[ (((texU)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0)]), (unsigned int)((0xffff-((texU)&0xffff))*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL15( depthmap(source[ (((texU)>>16)&0x3f) | (((texV)>>10)&0xfc0)]), (unsigned int)((0xffff-((texU)&0xffff))*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL15( depthmap(source[ ((((texU)+FRACUNIT)>>16)&0x3f) | (((texV)>>10)&0xfc0)]), (unsigned int)(((texU)&0xffff)*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS))) + +// do red and blue at once for slight speedup + +#define GETBLENDED15_5050(col1, col2) \ + ((((col1&0x7c1f)+(col2&0x7c1f))>>1)&0x7c1f) | \ + ((((col1&0x03e0)+(col2&0x03e0))>>1)&0x03e0) + +#define GETBLENDED16_5050(col1, col2) \ + ((((col1&0xf81f)+(col2&0xf81f))>>1)&0xf81f) | \ + ((((col1&0x07e0)+(col2&0x07e0))>>1)&0x07e0) + +#define GETBLENDED32_5050(col1, col2) \ + ((((col1&0xff00ff)+(col2&0xff00ff))>>1)&0xff00ff) | \ + ((((col1&0x00ff00)+(col2&0x00ff00))>>1)&0x00ff00) + +#define GETBLENDED15_3268(col1, col2) \ + ((((col1&0x7c1f)*5+(col2&0x7c1f)*11)>>4)&0x7c1f) | \ + ((((col1&0x03e0)*5+(col2&0x03e0)*11)>>4)&0x03e0) + +#define GETBLENDED16_3268(col1, col2) \ + ((((col1&0xf81f)*5+(col2&0xf81f)*11)>>4)&0xf81f) | \ + ((((col1&0x07e0)*5+(col2&0x07e0)*11)>>4)&0x07e0) + +#define GETBLENDED32_3268(col1, col2) \ + ((((col1&0xff00ff)*5+(col2&0xff00ff)*11)>>4)&0xff00ff) | \ + ((((col1&0x00ff00)*5+(col2&0x00ff00)*11)>>4)&0x00ff00) + +#define GETBLENDED15_9406(col1, col2) \ + ((((col1&0x7c1f)*15+(col2&0x7c1f))>>4)&0x7c1f) | \ + ((((col1&0x03e0)*15+(col2&0x03e0))>>4)&0x03e0) + +#define GETBLENDED16_9406(col1, col2) \ + ((((col1&0xf81f)*15+(col2&0xf81f))>>4)&0xf81f) | \ + ((((col1&0x07e0)*15+(col2&0x07e0))>>4)&0x07e0) + +#define GETBLENDED32_9406(col1, col2) \ + ((((col1&0xff00ff)*15+(col2&0xff00ff))>>4)&0xff00ff) | \ + ((((col1&0x00ff00)*15+(col2&0x00ff00))>>4)&0x00ff00) + +#endif diff --git a/src/r_fps.c b/src/r_fps.c new file mode 100644 index 0000000..fc1c64f --- /dev/null +++ b/src/r_fps.c @@ -0,0 +1,616 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, Andrey Budko + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Uncapped framerate stuff + * + *--------------------------------------------------------------------- + */ + +#include "doomstat.h" +#include "r_defs.h" +#include "r_state.h" +#include "p_spec.h" +#include "r_demo.h" +#include "r_fps.h" +#include "i_system.h" +#include "i_capture.h" +#include "e6y.h" + +int movement_smooth_default; +int movement_smooth; +dboolean isExtraDDisplay = false; + +typedef enum +{ + INTERP_SectorFloor, + INTERP_SectorCeiling, + INTERP_Vertex, + INTERP_WallPanning, + INTERP_FloorPanning, + INTERP_CeilingPanning +} interpolation_type_e; + +typedef struct +{ + interpolation_type_e type; + void *address; +} interpolation_t; + +int interpolation_maxobjects; + +static int numinterpolations = 0; + +tic_vars_t tic_vars; + +static void R_DoAnInterpolation (int i, fixed_t smoothratio); + +extern int realtic_clock_rate; +void D_Display(fixed_t frac); + +void M_ChangeUncappedFrameRate(void) +{ + if (capturing_video) + movement_smooth = true; + else + movement_smooth = (singletics ? false : movement_smooth_default); +} + +void R_InitInterpolation(void) +{ + tic_vars.msec = realtic_clock_rate * TICRATE / 100000.0f; +} + +typedef fixed_t fixed2_t[2]; +static fixed2_t *oldipos; +static fixed2_t *bakipos; +static interpolation_t *curipos; + +static dboolean NoInterpolateView; +static dboolean didInterp; +dboolean WasRenderedInTryRunTics; + +void R_InterpolateView(player_t *player, fixed_t frac) +{ + static mobj_t *oviewer; + + dboolean NoInterpolate = (paused && !walkcamera.type) || (menuactive && !demoplayback); + + viewplayer = player; + + if (player->mo != oviewer || NoInterpolate) + { + R_ResetViewInterpolation(); + oviewer = player->mo; + } + + if (NoInterpolate) + frac = FRACUNIT; + tic_vars.frac = frac; + + if (movement_smooth) + { + if (NoInterpolateView) + { + NoInterpolateView = false; + + player->prev_viewz = player->viewz; + player->prev_viewangle = player->mo->angle + viewangleoffset; + player->prev_viewpitch = player->mo->pitch + viewpitchoffset; + + P_ResetWalkcam(); + } + + if (walkcamera.type != 2) + { + viewx = player->mo->PrevX + FixedMul (frac, player->mo->x - player->mo->PrevX); + viewy = player->mo->PrevY + FixedMul (frac, player->mo->y - player->mo->PrevY); + viewz = player->prev_viewz + FixedMul (frac, player->viewz - player->prev_viewz); + } + else + { + viewx = walkcamera.PrevX + FixedMul (frac, walkcamera.x - walkcamera.PrevX); + viewy = walkcamera.PrevY + FixedMul (frac, walkcamera.y - walkcamera.PrevY); + viewz = walkcamera.PrevZ + FixedMul (frac, walkcamera.z - walkcamera.PrevZ); + } + + if (walkcamera.type) + { + viewangle = walkcamera.PrevAngle + FixedMul (frac, walkcamera.angle - walkcamera.PrevAngle); + viewpitch = walkcamera.PrevPitch + FixedMul (frac, walkcamera.pitch - walkcamera.PrevPitch); + } + else + { + viewangle = player->prev_viewangle + FixedMul (frac, R_SmoothPlaying_Get(player) - player->prev_viewangle) + viewangleoffset; + viewpitch = player->prev_viewpitch + FixedMul (frac, player->mo->pitch - player->prev_viewpitch) + viewpitchoffset; + } + } + else + { + if (walkcamera.type != 2) + { + viewx = player->mo->x; + viewy = player->mo->y; + viewz = player->viewz; + } + else + { + viewx = walkcamera.x; + viewy = walkcamera.y; + viewz = walkcamera.z; + } + if (walkcamera.type) + { + viewangle = walkcamera.angle; + viewpitch = walkcamera.pitch; + } + else + { + viewangle = R_SmoothPlaying_Get(player) + viewangleoffset; + viewpitch = player->mo->pitch + viewpitchoffset; + } + } + + if (!paused && movement_smooth) + { + int i; + + didInterp = tic_vars.frac != FRACUNIT; + if (didInterp) + { + for (i = numinterpolations - 1; i >= 0; i--) + { + R_DoAnInterpolation (i, tic_vars.frac); + } + } + } +} + +void R_ResetViewInterpolation () +{ + NoInterpolateView = true; +} + +static void R_CopyInterpToOld (int i) +{ + switch (curipos[i].type) + { + case INTERP_SectorFloor: + oldipos[i][0] = ((sector_t*)curipos[i].address)->floorheight; + break; + case INTERP_SectorCeiling: + oldipos[i][0] = ((sector_t*)curipos[i].address)->ceilingheight; + break; + case INTERP_Vertex: + oldipos[i][0] = ((vertex_t*)curipos[i].address)->x; + oldipos[i][1] = ((vertex_t*)curipos[i].address)->y; + break; + case INTERP_WallPanning: + oldipos[i][0] = ((side_t*)curipos[i].address)->rowoffset; + oldipos[i][1] = ((side_t*)curipos[i].address)->textureoffset; + break; + case INTERP_FloorPanning: + oldipos[i][0] = ((sector_t*)curipos[i].address)->floor_xoffs; + oldipos[i][1] = ((sector_t*)curipos[i].address)->floor_yoffs; + break; + case INTERP_CeilingPanning: + oldipos[i][0] = ((sector_t*)curipos[i].address)->ceiling_xoffs; + oldipos[i][1] = ((sector_t*)curipos[i].address)->ceiling_yoffs; + break; + } +} + +static void R_CopyBakToInterp (int i) +{ + switch (curipos[i].type) + { + case INTERP_SectorFloor: + ((sector_t*)curipos[i].address)->floorheight = bakipos[i][0]; + break; + case INTERP_SectorCeiling: + ((sector_t*)curipos[i].address)->ceilingheight = bakipos[i][0]; + break; + case INTERP_Vertex: + ((vertex_t*)curipos[i].address)->x = bakipos[i][0]; + ((vertex_t*)curipos[i].address)->y = bakipos[i][1]; + break; + case INTERP_WallPanning: + ((side_t*)curipos[i].address)->rowoffset = bakipos[i][0]; + ((side_t*)curipos[i].address)->textureoffset = bakipos[i][1]; + break; + case INTERP_FloorPanning: + ((sector_t*)curipos[i].address)->floor_xoffs = bakipos[i][0]; + ((sector_t*)curipos[i].address)->floor_yoffs = bakipos[i][1]; + break; + case INTERP_CeilingPanning: + ((sector_t*)curipos[i].address)->ceiling_xoffs = bakipos[i][0]; + ((sector_t*)curipos[i].address)->ceiling_yoffs = bakipos[i][1]; + break; + } +} + +static void R_DoAnInterpolation (int i, fixed_t smoothratio) +{ + fixed_t pos; + fixed_t *adr1 = NULL; + fixed_t *adr2 = NULL; + + switch (curipos[i].type) + { + case INTERP_SectorFloor: + adr1 = &((sector_t*)curipos[i].address)->floorheight; + break; + case INTERP_SectorCeiling: + adr1 = &((sector_t*)curipos[i].address)->ceilingheight; + break; + case INTERP_Vertex: + adr1 = &((vertex_t*)curipos[i].address)->x; +//// adr2 = &((vertex_t*)curipos[i].Address)->y; + break; + case INTERP_WallPanning: + adr1 = &((side_t*)curipos[i].address)->rowoffset; + adr2 = &((side_t*)curipos[i].address)->textureoffset; + break; + case INTERP_FloorPanning: + adr1 = &((sector_t*)curipos[i].address)->floor_xoffs; + adr2 = &((sector_t*)curipos[i].address)->floor_yoffs; + break; + case INTERP_CeilingPanning: + adr1 = &((sector_t*)curipos[i].address)->ceiling_xoffs; + adr2 = &((sector_t*)curipos[i].address)->ceiling_yoffs; + break; + + default: + return; + } + + if (adr1) + { + pos = bakipos[i][0] = *adr1; + *adr1 = oldipos[i][0] + FixedMul (pos - oldipos[i][0], smoothratio); + } + + if (adr2) + { + pos = bakipos[i][1] = *adr2; + *adr2 = oldipos[i][1] + FixedMul (pos - oldipos[i][1], smoothratio); + } + +#ifdef GL_DOOM + switch (curipos[i].type) + { + case INTERP_SectorFloor: + case INTERP_SectorCeiling: + gld_UpdateSplitData(((sector_t*)curipos[i].address)); + break; + } +#endif +} + +void R_UpdateInterpolations() +{ + int i; + if (!movement_smooth) + return; + for (i = numinterpolations-1; i >= 0; --i) + R_CopyInterpToOld (i); +} + +int interpolations_max = 0; + +static void R_SetInterpolation(interpolation_type_e type, void *posptr) +{ + int *i; + if (!movement_smooth) + return; + + if (numinterpolations >= interpolations_max) { + int prevmax = interpolations_max; + + interpolations_max = interpolations_max ? interpolations_max * 2 : 256; + + if (interpolation_maxobjects > 0 && interpolations_max > interpolation_maxobjects) + { + interpolations_max = interpolation_maxobjects; + } + + if (interpolations_max == prevmax) + { + return; + } + + oldipos = (fixed2_t*)realloc(oldipos, sizeof(*oldipos) * interpolations_max); + bakipos = (fixed2_t*)realloc(bakipos, sizeof(*bakipos) * interpolations_max); + curipos = (interpolation_t*)realloc(curipos, sizeof(*curipos) * interpolations_max); + } + + i = NULL; + switch (type) + { + case INTERP_SectorFloor: + i = &(((sector_t*)posptr)->INTERP_SectorFloor); + break; + case INTERP_SectorCeiling: + i = &(((sector_t*)posptr)->INTERP_SectorCeiling); + break; + case INTERP_WallPanning: + i = &(((side_t*)posptr)->INTERP_WallPanning); + break; + case INTERP_FloorPanning: + i = &(((sector_t*)posptr)->INTERP_FloorPanning); + break; + case INTERP_CeilingPanning: + i = &(((sector_t*)posptr)->INTERP_CeilingPanning); + break; + } + + if (i != NULL && (*i) == 0) + { + curipos[numinterpolations].address = posptr; + curipos[numinterpolations].type = type; + R_CopyInterpToOld (numinterpolations); + numinterpolations++; + (*i) = numinterpolations; + } +} + +static void R_StopInterpolation(interpolation_type_e type, void *posptr) +{ + int *i, *j; + void *posptr_last; + + if (!movement_smooth) + return; + + i = NULL; + switch (type) + { + case INTERP_SectorFloor: + i = &(((sector_t*)posptr)->INTERP_SectorFloor); + break; + case INTERP_SectorCeiling: + i = &(((sector_t*)posptr)->INTERP_SectorCeiling); + break; + case INTERP_WallPanning: + i = &(((side_t*)posptr)->INTERP_WallPanning); + break; + case INTERP_FloorPanning: + i = &(((sector_t*)posptr)->INTERP_FloorPanning); + break; + case INTERP_CeilingPanning: + i = &(((sector_t*)posptr)->INTERP_CeilingPanning); + break; + } + + if (i != NULL && (*i) != 0) + { + numinterpolations--; + + // we have +1 in index field of interpolation's parent + oldipos[*i - 1][0] = oldipos[numinterpolations][0]; + oldipos[*i - 1][1] = oldipos[numinterpolations][1]; + bakipos[*i - 1][0] = bakipos[numinterpolations][0]; + bakipos[*i - 1][1] = bakipos[numinterpolations][1]; + curipos[*i - 1] = curipos[numinterpolations]; + + // swap indexes + posptr_last = curipos[numinterpolations].address; + j = NULL; + switch (curipos[numinterpolations].type) + { + case INTERP_SectorFloor: + j = &(((sector_t*)posptr_last)->INTERP_SectorFloor); + break; + case INTERP_SectorCeiling: + j = &(((sector_t*)posptr_last)->INTERP_SectorCeiling); + break; + case INTERP_WallPanning: + j = &(((side_t*)posptr_last)->INTERP_WallPanning); + break; + case INTERP_FloorPanning: + j = &(((sector_t*)posptr_last)->INTERP_FloorPanning); + break; + case INTERP_CeilingPanning: + j = &(((sector_t*)posptr_last)->INTERP_CeilingPanning); + break; + } + + // swap + if (j != NULL) + { + *j = *i; + } + + // reset + *i = 0; + } +} + +void R_StopAllInterpolations(void) +{ + int i; + + if (!movement_smooth) + return; + + for(i=numinterpolations-1; i>= 0; --i) + { + numinterpolations--; + oldipos[i][0] = oldipos[numinterpolations][0]; + oldipos[i][1] = oldipos[numinterpolations][1]; + bakipos[i][0] = bakipos[numinterpolations][0]; + bakipos[i][1] = bakipos[numinterpolations][1]; + curipos[i] = curipos[numinterpolations]; + } + + for(i = 0; i < numsectors; i++) + { + sectors[i].INTERP_CeilingPanning = 0; + sectors[i].INTERP_FloorPanning = 0; + sectors[i].INTERP_SectorCeiling = 0; + sectors[i].INTERP_SectorFloor = 0; + } + + for(i = 0; i < numsides; i++) + { + sides[i].INTERP_WallPanning = 0; + } +} + +void R_RestoreInterpolations(void) +{ + int i; + + if (!movement_smooth) + return; + + if (didInterp) + { + didInterp = false; + for (i = numinterpolations-1; i >= 0; --i) + { + R_CopyBakToInterp (i); + } + } +} + +void R_ActivateSectorInterpolations() +{ + int i; + sector_t *sec; + + if (!movement_smooth) + return; + + for (i=0, sec = sectors ; ifloordata) + R_SetInterpolation (INTERP_SectorFloor, sec); + if (sec->ceilingdata) + R_SetInterpolation (INTERP_SectorCeiling, sec); + } +} + +static void R_InterpolationGetData(thinker_t *th, + interpolation_type_e *type1, interpolation_type_e *type2, + void **posptr1, void **posptr2) +{ + *posptr1 = NULL; + *posptr2 = NULL; + + if (th->function == T_MoveFloor) + { + *type1 = INTERP_SectorFloor; + *posptr1 = ((floormove_t *)th)->sector; + } + else + if (th->function == T_PlatRaise) + { + *type1 = INTERP_SectorFloor; + *posptr1 = ((plat_t *)th)->sector; + } + else + if (th->function == T_MoveCeiling) + { + *type1 = INTERP_SectorCeiling; + *posptr1 = ((ceiling_t *)th)->sector; + } + else + if (th->function == T_VerticalDoor) + { + *type1 = INTERP_SectorCeiling; + *posptr1 = ((vldoor_t *)th)->sector; + } + else + if (th->function == T_MoveElevator) + { + *type1 = INTERP_SectorFloor; + *posptr1 = ((elevator_t *)th)->sector; + *type2 = INTERP_SectorCeiling; + *posptr2 = ((elevator_t *)th)->sector; + } + else + if (th->function == T_Scroll) + { + switch (((scroll_t *)th)->type) + { + case sc_side: + *type1 = INTERP_WallPanning; + *posptr1 = sides + ((scroll_t *)th)->affectee; + break; + case sc_floor: + *type1 = INTERP_FloorPanning; + *posptr1 = sectors + ((scroll_t *)th)->affectee; + break; + case sc_ceiling: + *type1 = INTERP_CeilingPanning; + *posptr1 = sectors + ((scroll_t *)th)->affectee; + break; + default: ; + } + } +} + +void R_ActivateThinkerInterpolations(thinker_t *th) +{ + void *posptr1; + void *posptr2; + interpolation_type_e type1, type2; + + if (!movement_smooth) + return; + + R_InterpolationGetData(th, &type1, &type2, &posptr1, &posptr2); + + if(posptr1) + { + R_SetInterpolation (type1, posptr1); + + if(posptr2) + R_SetInterpolation (type2, posptr2); + } +} + +void R_StopInterpolationIfNeeded(thinker_t *th) +{ + void *posptr1; + void *posptr2; + interpolation_type_e type1, type2; + + if (!movement_smooth) + return; + + R_InterpolationGetData(th, &type1, &type2, &posptr1, &posptr2); + + if(posptr1) + { + R_StopInterpolation (type1, posptr1); + if(posptr2) + R_StopInterpolation (type2, posptr2); + } +} diff --git a/src/r_fps.h b/src/r_fps.h new file mode 100644 index 0000000..f8921fa --- /dev/null +++ b/src/r_fps.h @@ -0,0 +1,71 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, Andrey Budko + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Uncapped framerate stuff + * + *--------------------------------------------------------------------- + */ + +#ifndef __R_FPS__ +#define __R_FPS__ + +#include "doomstat.h" + +extern int movement_smooth_default; +extern int movement_smooth; +extern dboolean isExtraDDisplay; + +extern int interpolation_maxobjects; + +typedef struct { + unsigned int start; + unsigned int next; + unsigned int step; + fixed_t frac; + float msec; +} tic_vars_t; + +extern tic_vars_t tic_vars; + +void M_ChangeUncappedFrameRate(void); + +void R_InitInterpolation(void); +void R_InterpolateView(player_t *player, fixed_t frac); + +extern dboolean WasRenderedInTryRunTics; + +void R_ResetViewInterpolation (); +void R_UpdateInterpolations(); +void R_StopAllInterpolations(void); +void R_RestoreInterpolations(); +void R_ActivateSectorInterpolations(); +void R_ActivateThinkerInterpolations(thinker_t *th); +void R_StopInterpolationIfNeeded(thinker_t *th); + +#endif diff --git a/src/r_main.c b/src/r_main.c new file mode 100644 index 0000000..d2e2b79 --- /dev/null +++ b/src/r_main.c @@ -0,0 +1,1170 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Rendering main loop and setup functions, + * utility functions (BSP, geometry, trigonometry). + * See tables.c, too. + * + *-----------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "SDL.h" + +#include "doomstat.h" +#include "d_net.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_things.h" +#include "r_plane.h" +#include "r_bsp.h" +#include "r_draw.h" +#include "m_bbox.h" +#include "r_sky.h" +#include "v_video.h" +#include "lprintf.h" +#include "st_stuff.h" +#include "i_main.h" +#include "i_system.h" +#include "g_game.h" +#include "r_demo.h" +#include "r_fps.h" +#include +#include "e6y.h"//e6y +#include "xs_Float.h" + +// e6y +// Now they are variables. Depends from render_doom_lightmaps variable. +// Unify colour maping logic by cph is removed, because of bugs. +int LIGHTLEVELS = 32; +int LIGHTSEGSHIFT = 3; +int LIGHTBRIGHT = 2; +int render_doom_lightmaps; + +int r_frame_count; + +int r_have_internal_hires = false; + +// Fineangles in the SCREENWIDTH wide window. +#define FIELDOFVIEW 2048 + +// killough: viewangleoffset is a legacy from the pre-v1.2 days, when Doom +// had Left/Mid/Right viewing. +/-ANG90 offsets were placed here on each +// node, by d_net.c, to set up a L/M/R session. + +int viewangleoffset; +int viewpitchoffset; +int validcount = 1; // increment every time a check is made +const lighttable_t *fixedcolormap; +int centerx, centery; +// e6y: wide-res +int wide_centerx; +int wide_offsetx; +int wide_offset2x; +int wide_offsety; +int wide_offset2y; + +fixed_t focallength; +fixed_t focallengthy; +fixed_t globaluclip, globaldclip; +fixed_t centerxfrac, centeryfrac; +fixed_t yaspectmul; +fixed_t viewheightfrac; //e6y: for correct cliping of things +fixed_t projection; +// proff 11/06/98: Added for high-res +fixed_t projectiony; +fixed_t skyiscale; +fixed_t viewx, viewy, viewz; +angle_t viewangle; +fixed_t viewcos, viewsin; +fixed_t viewtancos, viewtansin; +player_t *viewplayer; +// e6y: Added for more precise flats drawing +fixed_t viewfocratio; + +int r_nearclip = 5; + +int FieldOfView; +int viewport[4]; +float modelMatrix[16]; +float projMatrix[16]; + +extern const lighttable_t **walllights; +extern const lighttable_t **walllightsnext; + +// +// 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. + +// e6y: resolution limitation is removed +angle_t *xtoviewangle; // killough 2/8/98 + +// killough 3/20/98: Support dynamic colormaps, e.g. deep water +// killough 4/4/98: support dynamic number of them as well + +int numcolormaps; +const lighttable_t *(*c_scalelight)[LIGHTLEVELS_MAX][MAXLIGHTSCALE]; +const lighttable_t *(*c_zlight)[LIGHTLEVELS_MAX][MAXLIGHTZ]; +const lighttable_t *(*scalelight)[MAXLIGHTSCALE]; +const lighttable_t *(*zlight)[MAXLIGHTZ]; +const lighttable_t *fullcolormap; +const lighttable_t **colormaps; +/* cph - allow crappy fake contrast to be disabled */ +int fake_contrast; + +// killough 3/20/98, 4/4/98: end dynamic colormaps + +//e6y: for Boom colormaps in OpenGL mode +dboolean use_boom_cm; +int boom_cm; // current colormap +int frame_fixedcolormap = 0; + +int extralight; // bumped light from gun blasts + +// +// R_PointOnSide +// Traverse BSP (sub) tree, +// check point against partition plane. +// Returns side 0 (front) or 1 (back). +// +// killough 5/2/98: reformatted +// + +// Workaround for optimization bug in clang +// fixes desync in competn/doom/fp2-3655.lmp and in dmnsns.wad dmn01m909.lmp +#if defined(__clang__) +PUREFUNC int R_PointOnSide(volatile fixed_t x, volatile fixed_t y, const node_t *node) +#else +PUREFUNC int R_PointOnSide(fixed_t x, fixed_t y, const node_t *node) +#endif +{ + if (!node->dx) + return x <= node->x ? node->dy > 0 : node->dy < 0; + + if (!node->dy) + return y <= node->y ? node->dx < 0 : node->dx > 0; + + x -= node->x; + y -= node->y; + + // Try to quickly decide by looking at sign bits. + if ((node->dy ^ node->dx ^ x ^ y) < 0) + return (node->dy ^ x) < 0; // (left is negative) + return FixedMul(y, node->dx>>FRACBITS) >= FixedMul(node->dy>>FRACBITS, x); +} + +// killough 5/2/98: reformatted + +PUREFUNC int R_PointOnSegSide(fixed_t x, fixed_t y, const seg_t *line) +{ + fixed_t lx = line->v1->x; + fixed_t ly = line->v1->y; + fixed_t ldx = line->v2->x - lx; + fixed_t ldy = line->v2->y - ly; + + if (!ldx) + return x <= lx ? ldy > 0 : ldy < 0; + + if (!ldy) + return y <= ly ? ldx < 0 : ldx > 0; + + x -= lx; + y -= ly; + + // Try to quickly decide by looking at sign bits. + if ((ldy ^ ldx ^ x ^ y) < 0) + return (ldy ^ x) < 0; // (left is negative) + return FixedMul(y, ldx>>FRACBITS) >= FixedMul(ldy>>FRACBITS, x); +} + +// +// R_PointToAngle +// To get a global angle from cartesian coordinates, +// the coordinates are flipped until they are in +// the first octant of the coordinate system, then +// the y (<=x) is scaled and divided by x to get a +// tangent (slope) value which is looked up in the +// tantoangle[] table. The +1 size of tantoangle[] +// is to handle the case when x==y without additional +// checking. +// +// killough 5/2/98: reformatted, cleaned up + +angle_t R_PointToAngleSlope(fixed_t x1, fixed_t y1, fixed_t x, fixed_t y, slope_div_fn slope_div) +{ + return (y -= y1, (x -= x1) || y) ? + x >= 0 ? + y >= 0 ? + (x > y) ? tantoangle[slope_div(y,x)] : // octant 0 + ANG90-1-tantoangle[slope_div(x,y)] : // octant 1 + x > (y = -y) ? 0-tantoangle[slope_div(y,x)] : // octant 8 + ANG270+tantoangle[slope_div(x,y)] : // octant 7 + y >= 0 ? (x = -x) > y ? ANG180-1-tantoangle[slope_div(y,x)] : // octant 3 + ANG90 + tantoangle[slope_div(x,y)] : // octant 2 + (x = -x) > (y = -y) ? ANG180+tantoangle[slope_div(y,x)] : // octant 4 + ANG270-1-tantoangle[slope_div(x,y)] : // octant 5 + 0; +} + +angle_t R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x, fixed_t y) +{ + return R_PointToAngleSlope(x1, y1, x, y, SlopeDiv); +} + +angle_t R_PointToAngleEx(fixed_t x, fixed_t y) +{ + return R_PointToAngleEx2(viewx, viewy, x, y); +} + +angle_t R_PointToAngleEx2(fixed_t x1, fixed_t y1, fixed_t x, fixed_t y) +{ + // [crispy] fix overflows for very long distances + int64_t y_viewy = (int64_t)y - y1; + int64_t x_viewx = (int64_t)x - x1; + + // [crispy] the worst that could happen is e.g. INT_MIN-INT_MAX = 2*INT_MIN + if (x_viewx < INT_MIN || x_viewx > INT_MAX ||y_viewy < INT_MIN || y_viewy > INT_MAX) + { + // [crispy] preserving the angle by halfing the distance in both directions + x = (int)(x_viewx / 2 + x1); + y = (int)(y_viewy / 2 + y1); + } + + return R_PointToAngleSlope(x1, y1, x, y, SlopeDivEx); +} + + +//----------------------------------------------------------------------------- +// +// ! Returns the pseudoangle between the line p1 to (infinity, p1.y) and the +// line from p1 to p2. The pseudoangle has the property that the ordering of +// points by true angle anround p1 and ordering of points by pseudoangle are the +// same. +// +// For clipping exact angles are not needed. Only the ordering matters. +// This is about as fast as the fixed point R_PointToAngle2 but without +// the precision issues associated with that function. +// +//----------------------------------------------------------------------------- + +angle_t R_PointToPseudoAngle (fixed_t x, fixed_t y) +{ + // Note: float won't work here as it's less precise than the BAM values being passed as parameters + double vecx = (double)x - viewx; + double vecy = (double)y - viewy; + + if (vecx == 0 && vecy == 0) + { + return 0; + } + else + { + double result = vecy / (fabs(vecx) + fabs(vecy)); + if (vecx < 0) + { + result = 2.0 - result; + } + return (angle_t)xs_CRoundToInt(result * (1 << 30)); + } +} + +// +// R_InitTextureMapping +// +// killough 5/2/98: reformatted + +static void R_InitTextureMapping (void) +{ + int i,x; + FieldOfView = FIELDOFVIEW; + + // For widescreen displays, increase the FOV so that the middle part of the + // screen that would be visible on a 4:3 display has the requested FOV. + if (wide_centerx != centerx) + { // wide_centerx is what centerx would be if the display was not widescreen + FieldOfView = (int)(atan((double)centerx * tan((double)FieldOfView * M_PI / FINEANGLES) / (double)wide_centerx) * FINEANGLES / M_PI); + if (FieldOfView > 160 * FINEANGLES / 360) + FieldOfView = 160 * FINEANGLES / 360; + } + + // Use tangent table to generate viewangletox: + // viewangletox will give the next greatest x + // after the view angle. + // + // Calc focallength + // so FIELDOFVIEW angles covers SCREENWIDTH. + + focallength = FixedDiv(centerxfrac, finetangent[FINEANGLES/4 + FieldOfView/2]); + focallengthy = Scale(centerxfrac, yaspectmul, finetangent[FINEANGLES/4 + FieldOfView/2]); + + for (i=0 ; i limit) + t = -1; + else + if (finetangent[i] < -limit) + t = viewwidth+1; + else + { + t = FixedMul(finetangent[i], focallength); + t = (centerxfrac - t + FRACUNIT-1) >> FRACBITS; + if (t < -1) + t = -1; + else + if (t > viewwidth+1) + t = viewwidth+1; + } + viewangletox[i] = t; + } + + // Scan viewangletox[] to generate xtoviewangle[]: + // xtoviewangle will give the smallest view angle + // that maps to x. + + for (x=0; x<=viewwidth; x++) + { + for (i=0; viewangletox[i] > x; i++) + ; + xtoviewangle[x] = (i<>= LIGHTSCALESHIFT)/DISTMAP; + + if (level < 0) + level = 0; + else + if (level >= NUMCOLORMAPS) + level = NUMCOLORMAPS-1; + + // killough 3/20/98: Initialize multiple colormaps + level *= 256; + for (t=0; t> FRACBITS > lastfrac >> FRACBITS) + { + lookup1[frac >> FRACBITS] = i; + lookup2[lastfrac >> FRACBITS] = i - 1; + + lastfrac = frac; + } + frac += step; + } + lookup2[max - 1] = size - 1; + lookup1[max] = lookup2[max] = size; + + for(i = 1; i < max; i++) + { + if (lookup1[i] == 0 && lookup1[i - 1] != 0) + { + lookup1[i] = lookup1[i - 1]; + } + if (lookup2[i] == 0 && lookup2[i - 1] != 0) + { + lookup2[i] = lookup2[i - 1]; + } + } +} + +static void InitStretchParam(stretch_param_t* offsets, int stretch, enum patch_translation_e flags) +{ + memset(offsets, 0, sizeof(*offsets)); + + switch (stretch) + { + case patch_stretch_16x10: + if (flags == VPT_ALIGN_WIDE) + { + offsets->video = &video_stretch; + offsets->deltax1 = (SCREENWIDTH - WIDE_SCREENWIDTH) / 2; + offsets->deltax2 = (SCREENWIDTH - WIDE_SCREENWIDTH) / 2; + } + else + { + offsets->video = &video; + offsets->deltax1 = wide_offsetx; + offsets->deltax2 = wide_offsetx; + } + break; + case patch_stretch_4x3: + offsets->video = &video_stretch; + offsets->deltax1 = (SCREENWIDTH - WIDE_SCREENWIDTH) / 2; + offsets->deltax2 = (SCREENWIDTH - WIDE_SCREENWIDTH) / 2; + break; + case patch_stretch_full: + offsets->video = &video_full; + offsets->deltax1 = 0; + offsets->deltax2 = 0; + break; + } + + if (flags == VPT_ALIGN_LEFT || flags == VPT_ALIGN_LEFT_BOTTOM || flags == VPT_ALIGN_LEFT_TOP) + { + offsets->deltax1 = 0; + offsets->deltax2 = 0; + } + + if (flags == VPT_ALIGN_RIGHT || flags == VPT_ALIGN_RIGHT_BOTTOM || flags == VPT_ALIGN_RIGHT_TOP) + { + offsets->deltax1 *= 2; + offsets->deltax2 *= 2; + } + + offsets->deltay1 = wide_offsety; + + if (flags == VPT_ALIGN_BOTTOM || flags == VPT_ALIGN_LEFT_BOTTOM || flags == VPT_ALIGN_RIGHT_BOTTOM) + { + offsets->deltay1 = wide_offset2y; + } + + if (flags == VPT_ALIGN_TOP || flags == VPT_ALIGN_LEFT_TOP || flags == VPT_ALIGN_RIGHT_TOP) + { + offsets->deltay1 = 0; + } + + if (flags == VPT_ALIGN_WIDE && !tallscreen) + { + offsets->deltay1 = 0; + } +} + +void R_SetupViewScaling(void) +{ + int i, k; + + for (i = 0; i < 3; i++) + { + for (k = 0; k < VPT_ALIGN_MAX; k++) + { + InitStretchParam(&stretch_params_table[i][k], i, k); + } + } + stretch_params = stretch_params_table[render_stretch_hud]; + + // SoM: ANYRES + // Moved stuff, reformatted a bit + // haleyjd 04/03/05: removed unnecessary FixedDiv calls + + video.xstep = ((320 << FRACBITS) / 320 / patches_scalex) + 1; + video.ystep = ((200 << FRACBITS) / 200 / patches_scaley) + 1; + video_stretch.xstep = ((320 << FRACBITS) / WIDE_SCREENWIDTH) + 1; + video_stretch.ystep = ((200 << FRACBITS) / WIDE_SCREENHEIGHT) + 1; + video_full.xstep = ((320 << FRACBITS) / SCREENWIDTH) + 1; + video_full.ystep = ((200 << FRACBITS) / SCREENHEIGHT) + 1; + + // SoM: ok, assemble the realx1/x2 arrays differently. To start, we are using floats + // to do the scaling which is 100 times more accurate, secondly, I realized that the + // reason the old single arrays were causing problems was they was only calculating the + // top-left corner of the scaled pixels. Calculating widths through these arrays is wrong + // because the scaling will change the final scaled widths depending on what their unscaled + // screen coords were. Thusly, all rectangles should be converted to unscaled x1, y1, x2, y2 + // coords, scaled, and then converted back to x, y, w, h + // + // e6y: wide-res + + video.width = 320 * patches_scalex; + video.height = 200 * patches_scaley; + GenLookup(video.x1lookup, video.x2lookup, video.width, 320, video.xstep); + GenLookup(video.y1lookup, video.y2lookup, video.height, 200, video.ystep); + + video_stretch.width = WIDE_SCREENWIDTH; + video_stretch.height = WIDE_SCREENHEIGHT; + GenLookup(video_stretch.x1lookup, video_stretch.x2lookup, video_stretch.width, 320, video_stretch.xstep); + GenLookup(video_stretch.y1lookup, video_stretch.y2lookup, video_stretch.height, 200, video_stretch.ystep); + + video_full.width = SCREENWIDTH; + video_full.height = SCREENHEIGHT; + GenLookup(video_full.x1lookup, video_full.x2lookup, video_full.width, 320, video_full.xstep); + GenLookup(video_full.y1lookup, video_full.y2lookup, video_full.height, 200, video_full.ystep); +} + +void R_MultMatrixVecd(const float matrix[16], const float in[4], float out[4]) +{ + int i; + + for (i = 0; i < 4; i++) + { + out[i] = + in[0] * matrix[0*4+i] + + in[1] * matrix[1*4+i] + + in[2] * matrix[2*4+i] + + in[3] * matrix[3*4+i]; + } +} + +int R_Project(float objx, float objy, float objz, float *winx, float *winy, float *winz) +{ + float in[4]; + float out[4]; + + in[0] = objx; + in[1] = objy; + in[2] = objz; + in[3] = 1.0f; + + R_MultMatrixVecd(modelMatrix, in, out); + R_MultMatrixVecd(projMatrix, out, in); + + if (in[3] == 0.0f) + return false; + + in[0] /= in[3]; + in[1] /= in[3]; + in[2] /= in[3]; + + /* Map x, y and z to range 0-1 */ + in[0] = in[0] * 0.5f + 0.5f; + in[1] = in[1] * 0.5f + 0.5f; + in[2] = in[2] * 0.5f + 0.5f; + + /* Map x,y to viewport */ + in[0] = in[0] * viewport[2] + viewport[0]; + in[1] = in[1] * viewport[3] + viewport[1]; + + *winx = in[0]; + *winy = in[1]; + *winz = in[2]; + + return true; +} + +void R_SetupViewport(void) +{ + extern int screenblocks; + int height; + + if (screenblocks == 11) + height = SCREENHEIGHT; + else if (screenblocks == 10) + height = SCREENHEIGHT; + else + height = (screenblocks*SCREENHEIGHT/10) & ~7; + + viewport[0] = viewwindowx; + viewport[1] = SCREENHEIGHT-(height+viewwindowy-((height-viewheight)/2)); + viewport[2] = viewwidth; + viewport[3] = height; +} + +void R_SetupPerspective(float fovy, float aspect, float znear) +{ + int i; + float focallength = 1.0f / (float)tan(fovy * (float)M_PI / 360.0f); + float *m = projMatrix; + + for (i = 0; i < 16; i++) + m[i] = 0.0f; + + m[0] = focallength / aspect; + m[5] = focallength; + m[10] = -1; + m[11] = -1; + m[14] = -2 * znear; +} + +void R_BuildModelViewMatrix(void) +{ + float x, y, z; + float yaw, pitch; + float A, B, C, D; + float *m = modelMatrix; + + yaw = 270.0f - (float)(viewangle>>ANGLETOFINESHIFT) * 360.0f / FINEANGLES; + yaw *= (float)M_PI / 180.0f; + pitch = 0; + if (V_GetMode() == VID_MODEGL) + { + pitch = (float)(viewpitch>>ANGLETOFINESHIFT) * 360.0f / FINEANGLES; + pitch *= (float)M_PI / 180.0f; + } + + x = (float)viewx / MAP_SCALE; + z = -(float)viewy / MAP_SCALE; + y = -(float)viewz / MAP_SCALE; + +/* + R_LoadIdentity(modelMatrix); + R_Rotate(modelMatrix, pitch, 0); + R_Rotate(modelMatrix, yaw, 1); + R_Translate(modelMatrix, x, y, z); +*/ + + A = (float)cos(pitch); + B = (float)sin(pitch); + C = (float)cos(yaw); + D = (float)sin(yaw); + + m[0] = C; + m[1] = D*B; + m[2] = -D*A; + m[3] = 0; + + m[4] = 0; + m[5] = A; + m[6] = B; + m[7] = 0; + + m[8] = D; + m[9] = -C*B; + m[10] = A*C; + m[11] = 0; + + m[12] = 0; + m[13] = 0; + m[14] = 0; + m[15] = 1; + + m[12] = m[0] * x + m[4] * y + m[8] * z + m[12]; + m[13] = m[1] * x + m[5] * y + m[9] * z + m[13]; + m[14] = m[2] * x + m[6] * y + m[10] * z + m[14]; + m[15] = m[3] * x + m[7] * y + m[11] * z + m[15]; +} + +// +// R_ExecuteSetViewSize +// + +void R_ExecuteSetViewSize (void) +{ + int i; + int cheight; + + setsizeneeded = false; + + SetRatio(SCREENWIDTH, SCREENHEIGHT); + + if (setblocks == 11) + { + scaledviewwidth = SCREENWIDTH; + viewheight = SCREENHEIGHT; + freelookviewheight = viewheight; + } +// proff 09/24/98: Added for high-res + else if (setblocks == 10) + { + scaledviewwidth = SCREENWIDTH; + viewheight = SCREENHEIGHT-ST_SCALED_HEIGHT; + freelookviewheight = SCREENHEIGHT; + } + else + { +// proff 08/17/98: Changed for high-res + scaledviewwidth = setblocks*SCREENWIDTH/10; + viewheight = (setblocks*(SCREENHEIGHT-ST_SCALED_HEIGHT)/10) & ~7; + freelookviewheight = setblocks*SCREENHEIGHT/10; + } + + viewwidth = scaledviewwidth; + + viewheightfrac = viewheight<4228 + projectiony = (fixed_t)((((int_64_t)cheight * centerx * 320) / 200) / SCREENWIDTH * FRACUNIT); + // e6y: this is a precalculated value for more precise flats drawing (see R_MapPlane) + viewfocratio = projectiony / wide_centerx; + + R_SetupViewScaling(); + + R_InitBuffer (scaledviewwidth, viewheight); + + R_InitTextureMapping(); + + // psprite scales + // proff 08/17/98: Changed for high-res + // proff 11/06/98: Added for high-res + // e6y: wide-res + pspritexscale = (wide_centerx << FRACBITS) / 160; + pspriteyscale = (((cheight*viewwidth)/SCREENWIDTH) << FRACBITS) / 200; + pspriteiscale = FixedDiv (FRACUNIT, pspritexscale); + // [FG] make sure that the product of the weapon sprite scale factor + // and its reciprocal is always at least FRACUNIT to + // fix garbage lines at the top of weapon sprites + pspriteiyscale = FixedDiv (FRACUNIT, pspriteyscale); + while (FixedMul(pspriteiyscale, pspriteyscale) < FRACUNIT) + pspriteiyscale++; + + //e6y: added for GL + pspritexscale_f = (float)wide_centerx/160.0f; + pspriteyscale_f = (((float)cheight*viewwidth)/(float)SCREENWIDTH) / 200.0f; + + skyiscale = (fixed_t)(((uint_64_t)FRACUNIT * SCREENWIDTH * 200) / (viewwidth * SCREENHEIGHT)); + + // [RH] Sky height fix for screens not 200 (or 240) pixels tall + R_InitSkyMap(); + + // thing clipping + for (i=0 ; i>ANGLETOFINESHIFT]); + distscale[i] = FixedDiv(FRACUNIT,cosadj); + } + + // e6y + // Calculate the light levels to use + // for each level / scale combination. + // Calculate the light levels to use + // for each level / scale combination. + for (i=0; i= NUMCOLORMAPS) + level = NUMCOLORMAPS-1; + + // killough 3/20/98: initialize multiple colormaps + level *= 256; + + for (t=0; t>ANGLETOFINESHIFT]); + centery += dy >> FRACBITS; + } + centeryfrac = centery<extralight; + + viewsin = finesine[viewangle>>ANGLETOFINESHIFT]; + viewcos = finecosine[viewangle>>ANGLETOFINESHIFT]; + + viewtansin = FixedMul(FocalTangent, viewsin); + viewtancos = FixedMul(FocalTangent, viewcos); + + R_SetupFreelook(); + + // killough 3/20/98, 4/4/98: select colormap based on player status + + if (player->mo->subsector->sector->heightsec != -1) + { + const sector_t *s = player->mo->subsector->sector->heightsec + sectors; + cm = viewz < s->floorheight ? s->bottommap : viewz > s->ceilingheight ? + s->topmap : s->midmap; + if (cm < 0 || cm > numcolormaps) + cm = 0; + } + else + cm = 0; + + //e6y: save previous and current colormap + boom_cm = cm; + + fullcolormap = colormaps[cm]; + zlight = c_zlight[cm]; + scalelight = c_scalelight[cm]; + + //e6y + frame_fixedcolormap = player->fixedcolormap; + if (frame_fixedcolormap < 0 || frame_fixedcolormap > NUMCOLORMAPS) + { + I_Error(" value out of range: %d\n", player->fixedcolormap); + } + + if (player->fixedcolormap) + { + // killough 3/20/98: localize scalelightfixed (readability/optimization) + static const lighttable_t *scalelightfixed[MAXLIGHTSCALE]; + + fixedcolormap = fullcolormap // killough 3/20/98: use fullcolormap + + player->fixedcolormap*256*sizeof(lighttable_t); + + walllights = scalelightfixed; + walllightsnext = scalelightfixed; + + for (i=0 ; i= FPS_SavedTick + 1000) + { + renderer_fps = 1000 * FPS_FrameCount / (tick - FPS_SavedTick); + if (rendering_stats) + { + doom_printf((V_GetMode() == VID_MODEGL) + ?"Frame rate %d fps\nWalls %d, Flats %d, Sprites %d" + :"Frame rate %d fps\nSegs %d, Visplanes %d, Sprites %d", + renderer_fps, rendered_segs, rendered_visplanes, rendered_vissprites); + } + FPS_SavedTick = tick; + FPS_FrameCount = 0; + } +} + +void R_ClearStats(void) +{ + rendered_visplanes = 0; + rendered_segs = 0; + rendered_vissprites = 0; +} + +// +// R_RenderView +// +void R_RenderPlayerView (player_t* player) +{ + dboolean automap = (automapmode & am_active) && !(automapmode & am_overlay); + + r_frame_count++; + + R_SetupFrame (player); + + // Clear buffers. + R_ClearClipSegs (); + R_ClearDrawSegs (); + R_ClearPlanes (); + R_ClearSprites (); + + if (V_GetMode() == VID_MODEGL) + { +#ifdef GL_DOOM + // proff 11/99: clear buffers + gld_InitDrawScene(); + + if (!automap) + { + // proff 11/99: switch to perspective mode + gld_StartDrawScene(); + } +#endif + } else { + if (flashing_hom) + { // killough 2/10/98: add flashing red HOM indicators + unsigned char color=(gametic % 20) < 9 ? 0xb0 : 0; + V_FillRect(0, viewwindowx, viewwindowy, viewwidth, viewheight, color); + R_DrawViewBorder(); + } + } + + // check for new console commands. +#ifdef HAVE_NET + NetUpdate (); +#endif + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) { + { + angle_t a1 = gld_FrustumAngle(); + gld_clipper_Clear(); + gld_clipper_SafeAddClipRangeRealAngles(viewangle + a1, viewangle - a1); + gld_FrustrumSetup(); + } + } +#endif + + // The head node is the last node output. + R_RenderBSPNode (numnodes-1); + +#ifdef HAVE_NET + NetUpdate (); +#endif + + if (V_GetMode() != VID_MODEGL) + R_DrawPlanes(); + + R_ResetColumnBuffer(); + + // Check for new console commands. +#ifdef HAVE_NET + NetUpdate (); +#endif + + if (V_GetMode() != VID_MODEGL) { + R_DrawMasked (); + R_ResetColumnBuffer(); + } + + // Check for new console commands. +#ifdef HAVE_NET + NetUpdate (); +#endif + + if (V_GetMode() == VID_MODEGL && !automap) { +#ifdef GL_DOOM + // proff 11/99: draw the scene + gld_DrawScene(player); + // proff 11/99: finishing off + gld_EndDrawScene(); +#endif + } +} diff --git a/src/r_main.h b/src/r_main.h new file mode 100644 index 0000000..a9a3d65 --- /dev/null +++ b/src/r_main.h @@ -0,0 +1,180 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Renderer main interface. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_MAIN__ +#define __R_MAIN__ + +#include "d_player.h" +#include "r_data.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +extern int r_frame_count; + +// +// POV related. +// + +extern fixed_t viewcos; +extern fixed_t viewsin; +extern fixed_t viewtancos; +extern fixed_t viewtansin; +extern int viewwidth; +extern int viewheight; +extern int viewwindowx; +extern int viewwindowy; +extern int centerx; +extern int centery; +extern fixed_t globaluclip; +extern fixed_t globaldclip; +extern fixed_t centerxfrac; +extern fixed_t centeryfrac; +extern fixed_t yaspectmul; +extern fixed_t viewheightfrac; //e6y: for correct cliping of things +extern fixed_t projection; +extern fixed_t skyiscale; +// e6y: wide-res +extern int wide_centerx; +extern int wide_offsetx; +extern int wide_offset2x; +extern int wide_offsety; +extern int wide_offset2y; +#define RMUL (1.6f/1.333333f) + +// proff 11/06/98: Added for high-res +extern fixed_t projectiony; +extern int validcount; +// e6y: Added for more precise flats drawing +extern fixed_t viewfocratio; + +// +// Rendering stats +// + +extern int rendered_visplanes, rendered_segs, rendered_vissprites; +extern dboolean rendering_stats; + +// +// Lighting LUT. +// Used for z-depth cuing per column/row, +// and other lighting effects (sector ambient, flash). +// + +// Lighting constants. + +// SoM: I am really speechless at this... just... why? +// Lighting in doom was originally clamped off to just 16 brightness levels +// for sector lighting. Simply changing the constants is enough to change this +// it seriously bottles the mind why this wasn't done in doom from the start +// except for maybe memory usage savings. +#define LIGHTLEVELS_MAX 32 + +extern int LIGHTSEGSHIFT; +extern int LIGHTBRIGHT; +extern int LIGHTLEVELS; +extern int render_doom_lightmaps; + +#define MAXLIGHTSCALE 48 +#define LIGHTSCALESHIFT 12 +#define MAXLIGHTZ 128 +#define LIGHTZSHIFT 20 + +/* cph - allow crappy fake contrast to be disabled */ +extern int fake_contrast; + +// killough 3/20/98: Allow colormaps to be dynamic (e.g. underwater) +extern const lighttable_t *(*scalelight)[MAXLIGHTSCALE]; +extern const lighttable_t *(*c_zlight)[LIGHTLEVELS_MAX][MAXLIGHTZ]; +extern const lighttable_t *(*zlight)[MAXLIGHTZ]; +extern const lighttable_t *fullcolormap; +extern int numcolormaps; // killough 4/4/98: dynamic number of maps +extern const lighttable_t **colormaps; +// killough 3/20/98, 4/4/98: end dynamic colormaps + +//e6y: for Boom colormaps in OpenGL mode +extern dboolean use_boom_cm; +extern int boom_cm; // current colormap +extern int frame_fixedcolormap; + +extern int extralight; +extern const lighttable_t *fixedcolormap; + +// Number of diminishing brightness levels. +// There a 0-31, i.e. 32 LUT in the COLORMAP lump. + +#define NUMCOLORMAPS 32 +// Index of the special effects (INVUL inverse) map. +#define INVERSECOLORMAP 32 + +// +// Utility functions. +// + +PUREFUNC int R_PointOnSide(fixed_t x, fixed_t y, const node_t *node); +PUREFUNC int R_PointOnSegSide(fixed_t x, fixed_t y, const seg_t *line); +angle_t R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x, fixed_t y); +subsector_t *R_PointInSubsector(fixed_t x, fixed_t y); + +//e6y: made more precise +angle_t R_PointToAngleEx(fixed_t x, fixed_t y); +angle_t R_PointToAngleEx2(fixed_t x1, fixed_t y1, fixed_t x, fixed_t y); +angle_t R_PointToPseudoAngle(fixed_t x, fixed_t y); + +extern int r_have_internal_hires; + +// +// REFRESH - the actual rendering functions. +// + +void R_RenderPlayerView(player_t *player); // Called by G_Drawer. +void R_Init(void); // Called by startup code. +void R_SetViewSize(int blocks); // Called by M_Responder. +void R_ExecuteSetViewSize(void); // cph - called by D_Display to complete a view resize + +void R_ShowStats(void); +void R_ClearStats(void); + +#define Pi 3.14159265358979323846f +#define DEG2RAD(a) ((a * Pi) / 180.0f) +#define RAD2DEG(a) ((a / Pi) * 180.0f) +#define MAP_COEFF 128.0f +#define MAP_SCALE (MAP_COEFF*(float)FRACUNIT) + +extern int viewport[4]; +extern float modelMatrix[16]; +extern float projMatrix[16]; +int R_Project(float objx, float objy, float objz, float *winx, float *winy, float *winz); + +#endif diff --git a/src/r_patch.c b/src/r_patch.c new file mode 100644 index 0000000..d9930cc --- /dev/null +++ b/src/r_patch.c @@ -0,0 +1,991 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + +/* +**--------------------------------------------------------------------------- +** Copyright 2004-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +*/ + +#include "z_zone.h" +#include "doomstat.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_sky.h" +#include "r_bsp.h" +#include "r_things.h" +#include "p_tick.h" +#include "i_system.h" +#include "r_draw.h" +#include "lprintf.h" +#include "r_patch.h" +#include "v_video.h" +#include + +// posts are runs of non masked source pixels +typedef struct +{ + byte topdelta; // -1 is the last post in a column + byte length; // length data bytes follows +} post_t; + +// column_t is a list of 0 or more post_t, (byte)-1 terminated +typedef post_t column_t; + +// +// Patches. +// A patch holds one or more columns. +// Patches are used for sprites and all masked pictures, +// and we compose textures from the TEXTURE1/2 lists +// of patches. +// + +typedef struct +{ + short width, height; // bounding box size + short leftoffset; // pixels to the left of origin + short topoffset; // pixels below the origin + int columnofs[8]; // only [width] used +} patch_t; + +//--------------------------------------------------------------------------- +// Re-engineered patch support +//--------------------------------------------------------------------------- +static rpatch_t *patches = 0; + +static rpatch_t *texture_composites = 0; + +// indices of two duplicate PLAYPAL entries, second is -1 if none found +static int playpal_transparent, playpal_duplicate; + +static dboolean DuplicatePaletteEntry(const char *playpal, int i, int j) { + int colormap_i; + + if (playpal[3 * i + 0] != playpal[3 * j + 0] || + playpal[3 * i + 1] != playpal[3 * j + 1] || + playpal[3 * i + 2] != playpal[3 * j + 2]) + return false; + + for (colormap_i = 0; colormap_i < NUMCOLORMAPS; ++colormap_i) + if (colormaps[0][colormap_i * 256 + i] != colormaps[0][colormap_i * 256 + j]) + return false; + + return true; +} + +//--------------------------------------------------------------------------- +void R_InitPatches(void) { + if (!patches) + { + patches = malloc(numlumps * sizeof(rpatch_t)); + // clear out new patches to signal they're uninitialized + memset(patches, 0, sizeof(rpatch_t)*numlumps); + } + if (!texture_composites) + { + texture_composites = malloc(numtextures * sizeof(rpatch_t)); + // clear out new patches to signal they're uninitialized + memset(texture_composites, 0, sizeof(rpatch_t)*numtextures); + } + + if (!playpal_duplicate) + { + int lump = W_GetNumForName("PLAYPAL"); + const byte *playpal = W_CacheLumpNum(lump); + + // find two duplicate palette entries. use one for transparency. + // rewrite source pixels in patches to the other on composition. + + int i, j, found = 0; + + for (i = 0; i < 256; i++) + { + for (j = i+1; j < 256; j++) + { + if (DuplicatePaletteEntry(playpal, i, j)) + { + found = 1; + break; + } + } + if (found) + break; + } + + if (found) { // found duplicate + playpal_transparent = i; + playpal_duplicate = j; + } else { // no duplicate: use 255 for transparency, as done previously + playpal_transparent = 255; + playpal_duplicate = -1; + } + + W_UnlockLumpNum(lump); + } +} + +//--------------------------------------------------------------------------- +void R_FlushAllPatches(void) { + int i; + + if (patches) + { + for (i=0; i < numlumps; i++) + if (patches[i].locks > 0) + I_Error("R_FlushAllPatches: patch number %i still locked",i); + free(patches); + patches = NULL; + } + if (texture_composites) + { + for (i=0; iwidth; + R_UnlockPatchNum(lump); + return width; +} + +//--------------------------------------------------------------------------- +int R_NumPatchHeight(int lump) +{ + const rpatch_t *patch = R_CachePatchNum(lump); + int height = patch->height; + R_UnlockPatchNum(lump); + return height; +} + +//--------------------------------------------------------------------------- +static int getPatchIsNotTileable(const patch_t *patch) { + int x=0, numPosts, lastColumnDelta = 0; + const column_t *column; + int cornerCount = 0; + int hasAHole = 0; + + for (x=0; xwidth); x++) { + column = (const column_t *)((const byte *)patch + LittleLong(patch->columnofs[x])); + if (!x) lastColumnDelta = column->topdelta; + else if (lastColumnDelta != column->topdelta) hasAHole = 1; + + numPosts = 0; + while (column->topdelta != 0xff) { + // check to see if a corner pixel filled + if (x == 0 && column->topdelta == 0) cornerCount++; + else if (x == 0 && column->topdelta + column->length >= LittleShort(patch->height)) cornerCount++; + else if (x == LittleShort(patch->width)-1 && column->topdelta == 0) cornerCount++; + else if (x == LittleShort(patch->width)-1 && column->topdelta + column->length >= LittleShort(patch->height)) cornerCount++; + + if (numPosts++) hasAHole = 1; + column = (const column_t *)((const byte *)column + column->length + 4); + } + } + + if (cornerCount == 4) return 0; + return hasAHole; +} + +//--------------------------------------------------------------------------- +static int getIsSolidAtSpot(const column_t *column, int spot) { + if (!column) return 0; + while (column->topdelta != 0xff) { + if (spot < column->topdelta) return 0; + if ((spot >= column->topdelta) && (spot <= column->topdelta + column->length)) return 1; + column = (const column_t*)((const byte*)column + 3 + column->length + 1); + } + return 0; +} + +//--------------------------------------------------------------------------- +// Used to determine whether a column edge (top or bottom) should slope +// up or down for smoothed masked edges - POPE +//--------------------------------------------------------------------------- +static int getColumnEdgeSlope(const column_t *prevcolumn, const column_t *nextcolumn, int spot) { + int holeToLeft = !getIsSolidAtSpot(prevcolumn, spot); + int holeToRight = !getIsSolidAtSpot(nextcolumn, spot); + + if (holeToLeft && !holeToRight) return 1; + if (!holeToLeft && holeToRight) return -1; + return 0; +} + +//--------------------------------------------------------------------------- +static void FillEmptySpace(rpatch_t *patch) +{ + int x, y, w, h, numpix, pass, transparent, has_holes; + byte *orig, *copy, *src, *dest, *prev, *next; + + // loop over patch looking for transparent pixels next to solid ones + // copy solid pixels into the spaces, dilating the patch outwards + // repeat either a fixed number of times or until all space is filled + // this eliminates artifacts surrounding weapon sprites (e.g. chainsaw) + + w = patch->width; + h = patch->height; + numpix = w * h; + + // alternate between two buffers to avoid "overlapping memcpy"-like symptoms + orig = patch->pixels; + copy = malloc(numpix); + + for (pass = 0; pass < 8; pass++) // arbitrarily chosen limit (must be even) + { + // copy src to dest, then switch them on next pass + // (this requires an even number of passes) + src = ((pass & 1) == 0) ? orig : copy; + dest = ((pass & 1) == 0) ? copy : orig; + + // previous and next columns adjacent to current column x + // these pointers must not be dereferenced without appropriate checks + prev = src - h; // only valid when x > 0 + next = src + h; // only valid when x < w-1 + + has_holes = 0; // if the patch has any holes at all + transparent = 0; // number of pixels this pass did not handle + + // detect transparent pixels on edges, copy solid colour into the space + // the order of directions (up,down,left,right) is arbitrarily chosen + for (x = 0; x < w; x++) + { + for (y = 0; y < h; y++) + { + if (*src == playpal_transparent) has_holes = 1; + + if (*src != playpal_transparent) + *dest = *src; // already a solid pixel, just copy it over + else if (y > 0 && *(src-1) != playpal_transparent) + *dest = *(src - 1); // solid pixel above + else if (y < h-1 && *(src+1) != playpal_transparent) + *dest = *(src + 1); // solid pixel below + else if (x > 0 && *prev != playpal_transparent) + *dest = *prev; // solid pixel to left + else if (x < w-1 && *next != playpal_transparent) + *dest = *next; // solid pixel to right + else // transparent pixel with no adjacent solid pixels + *dest = *src, transparent++; // count unhandled pixels + + prev++, src++, next++, dest++; + } + } + + if (transparent == 0) // no more transparent pixels to fill + { + if ((pass & 1) == 0) // dest was copy, src was orig: orig needs update + memcpy(orig, copy, numpix); + break; + } + else if (transparent == numpix) + break; // avoid infinite loop on entirely transparent patches (STBR127) + } + + free(copy); + + // copy top row of patch into any space at bottom, and vice versa + // a hack to fix erroneous row of pixels at top of firing chaingun + + for (x = 0, src = orig, dest = src + h-1; x < w; x++, src += h, dest += h) + { + if (*src != playpal_transparent && *dest == playpal_transparent) + *dest = *src; // bottom transparent, top solid + else if (*src == playpal_transparent && *dest != playpal_transparent) + *src = *dest; // top transparent, bottom solid + } + + if (has_holes) + patch->flags |= PATCH_HASHOLES; +} + +//========================================================================== +// +// Checks if the lump can be a Doom patch +// +//========================================================================== + +static dboolean CheckIfPatch(int lump) +{ + int size; + int width, height; + const patch_t * patch; + dboolean result; + + size = W_LumpLength(lump); + + // minimum length of a valid Doom patch + if (size < 13) + return false; + + patch = (const patch_t *)W_CacheLumpNum(lump); + + width = LittleShort(patch->width); + height = LittleShort(patch->height); + + result = (height > 0 && height <= 16384 && width > 0 && width <= 16384 && width < size / 4); + + if (result) + { + // The dimensions seem like they might be valid for a patch, so + // check the column directory for extra security. All columns + // must begin after the column directory, and none of them must + // point past the end of the patch. + int x; + + for (x = 0; x < width; x++) + { + unsigned int ofs = LittleLong(patch->columnofs[x]); + + // Need one byte for an empty column (but there's patches that don't know that!) + if (ofs < (unsigned int)width * 4 + 8 || ofs >= (unsigned int)size) + { + result = false; + break; + } + } + } + + W_UnlockLumpNum(lump); + return result; +} + +//--------------------------------------------------------------------------- +static void StorePixel(rpatch_t *patch, int x, int y, byte color) +{ + // write pixel to patch, substituting for playpal_transparent as needed + if (color == playpal_transparent && playpal_duplicate >= 0) + color = playpal_duplicate; + patch->pixels[x * patch->height + y] = color; +} + +//--------------------------------------------------------------------------- +static void createPatch(int id) { + rpatch_t *patch; + const int patchNum = id; + const patch_t *oldPatch; + const column_t *oldColumn, *oldPrevColumn, *oldNextColumn; + int x, y; + int pixelDataSize; + int columnsDataSize; + int postsDataSize; + int dataSize; + int *numPostsInColumn; + int numPostsTotal; + const unsigned char *oldColumnPixelData; + int numPostsUsedSoFar; + int edgeSlope; + +#ifdef RANGECHECK + if (id >= numlumps) + I_Error("createPatch: %i >= numlumps", id); +#endif + + if (!CheckIfPatch(patchNum)) + { + I_Error("createPatch: Unknown patch format %s.", + (patchNum < numlumps ? lumpinfo[patchNum].name : NULL)); + } + + oldPatch = (const patch_t*)W_CacheLumpNum(patchNum); + + patch = &patches[id]; + // proff - 2003-02-16 What about endianess? + patch->width = LittleShort(oldPatch->width); + patch->widthmask = 0; + patch->height = LittleShort(oldPatch->height); + patch->leftoffset = LittleShort(oldPatch->leftoffset); + patch->topoffset = LittleShort(oldPatch->topoffset); + patch->flags = 0; + if (getPatchIsNotTileable(oldPatch)) + patch->flags |= PATCH_ISNOTTILEABLE; + +#ifdef GL_DOOM + // Width of M_THERMM patch is 9, but Doom interprets it as 8-columns lump + // during drawing. It is not a problem for software mode and GL_NEAREST, + // but looks wrong with filtering. So I need to patch it during loading. + if (V_GetMode() == VID_MODEGL) + { + if (!strncasecmp(lumpinfo[id].name, "M_THERMM", 8) && patch->width > 8) + { + patch->width--; + } + } +#endif + + // work out how much memory we need to allocate for this patch's data + pixelDataSize = (patch->width * patch->height + 4) & ~3; + columnsDataSize = sizeof(rcolumn_t) * patch->width; + + // count the number of posts in each column + numPostsInColumn = malloc(sizeof(int) * patch->width); + numPostsTotal = 0; + + for (x=0; xwidth; x++) { + oldColumn = (const column_t *)((const byte *)oldPatch + LittleLong(oldPatch->columnofs[x])); + numPostsInColumn[x] = 0; + while (oldColumn->topdelta != 0xff) { + numPostsInColumn[x]++; + numPostsTotal++; + oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4); + } + } + + postsDataSize = numPostsTotal * sizeof(rpost_t); + + // allocate our data chunk + dataSize = pixelDataSize + columnsDataSize + postsDataSize; + patch->data = (unsigned char*)Z_Malloc(dataSize, PU_CACHE, (void **)&patch->data); + memset(patch->data, 0, dataSize); + + // set out pixel, column, and post pointers into our data array + patch->pixels = patch->data; + patch->columns = (rcolumn_t*)((unsigned char*)patch->pixels + pixelDataSize); + patch->posts = (rpost_t*)((unsigned char*)patch->columns + columnsDataSize); + + // sanity check that we've got all the memory allocated we need + assert((((byte*)patch->posts + numPostsTotal*sizeof(rpost_t)) - (byte*)patch->data) == dataSize); + + if (playpal_transparent != 0) + memset(patch->pixels, playpal_transparent, (patch->width*patch->height)); + + // fill in the pixels, posts, and columns + numPostsUsedSoFar = 0; + for (x=0; xwidth; x++) { + int top = -1; + + oldColumn = (const column_t *)((const byte *)oldPatch + LittleLong(oldPatch->columnofs[x])); + + if (patch->flags&PATCH_ISNOTTILEABLE) { + // non-tiling + if (x == 0) oldPrevColumn = 0; + else oldPrevColumn = (const column_t *)((const byte *)oldPatch + LittleLong(oldPatch->columnofs[x-1])); + if (x == patch->width-1) oldNextColumn = 0; + else oldNextColumn = (const column_t *)((const byte *)oldPatch + LittleLong(oldPatch->columnofs[x+1])); + } + else { + // tiling + int prevColumnIndex = x-1; + int nextColumnIndex = x+1; + while (prevColumnIndex < 0) prevColumnIndex += patch->width; + while (nextColumnIndex >= patch->width) nextColumnIndex -= patch->width; + oldPrevColumn = (const column_t *)((const byte *)oldPatch + LittleLong(oldPatch->columnofs[prevColumnIndex])); + oldNextColumn = (const column_t *)((const byte *)oldPatch + LittleLong(oldPatch->columnofs[nextColumnIndex])); + } + + // setup the column's data + patch->columns[x].pixels = patch->pixels + (x*patch->height) + 0; + patch->columns[x].numPosts = numPostsInColumn[x]; + patch->columns[x].posts = patch->posts + numPostsUsedSoFar; + + while (oldColumn->topdelta != 0xff) { + int len = oldColumn->length; + + //e6y: support for DeePsea's true tall patches + if (oldColumn->topdelta <= top) + { + top += oldColumn->topdelta; + } + else + { + top = oldColumn->topdelta; + } + + // Clip posts that extend past the bottom + if (top + oldColumn->length > patch->height) + { + len = patch->height - top; + } + + if (len > 0) + { + // set up the post's data + patch->posts[numPostsUsedSoFar].topdelta = top; + patch->posts[numPostsUsedSoFar].length = len; + patch->posts[numPostsUsedSoFar].slope = 0; + + edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, top); + if (edgeSlope == 1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_TOP_UP; + else if (edgeSlope == -1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_TOP_DOWN; + + edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, top+len); + if (edgeSlope == 1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_BOT_UP; + else if (edgeSlope == -1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_BOT_DOWN; + + // fill in the post's pixels + oldColumnPixelData = (const byte *)oldColumn + 3; + for (y=0; ylength + 4); + numPostsUsedSoFar++; + } + } + + FillEmptySpace(patch); + + W_UnlockLumpNum(patchNum); + free(numPostsInColumn); +} + +typedef struct { + unsigned short patches; + unsigned short posts; + unsigned short posts_used; +} count_t; + +static void switchPosts(rpost_t *post1, rpost_t *post2) { + rpost_t dummy; + + dummy.topdelta = post1->topdelta; + dummy.length = post1->length; + dummy.slope = post1->slope; + post1->topdelta = post2->topdelta; + post1->length = post2->length; + post1->slope = post2->slope; + post2->topdelta = dummy.topdelta; + post2->length = dummy.length; + post2->slope = dummy.slope; +} + +static void removePostFromColumn(rcolumn_t *column, int post) { + int i; +#ifdef RANGECHECK + if (post >= column->numPosts) + I_Error("removePostFromColumn: invalid post index"); +#endif + if (post < column->numPosts) + for (i=post; i<(column->numPosts-1); i++) { + rpost_t *post1 = &column->posts[i]; + rpost_t *post2 = &column->posts[i+1]; + post1->topdelta = post2->topdelta; + post1->length = post2->length; + post1->slope = post2->slope; + } + column->numPosts--; +} + +//--------------------------------------------------------------------------- +static void createTextureCompositePatch(int id) { + rpatch_t *composite_patch; + texture_t *texture; + texpatch_t *texpatch; + int patchNum; + const patch_t *oldPatch; + const column_t *oldColumn, *oldPrevColumn, *oldNextColumn; + int i, x, y; + int oy, count; + int pixelDataSize; + int columnsDataSize; + int postsDataSize; + int dataSize; + int numPostsTotal; + const unsigned char *oldColumnPixelData; + int numPostsUsedSoFar; + int edgeSlope; + count_t *countsInColumn; + +#ifdef RANGECHECK + if (id >= numtextures) + I_Error("createTextureCompositePatch: %i >= numtextures", id); +#endif + + composite_patch = &texture_composites[id]; + + texture = textures[id]; + + composite_patch->width = texture->width; + composite_patch->height = texture->height; + composite_patch->widthmask = texture->widthmask; + composite_patch->leftoffset = 0; + composite_patch->topoffset = 0; + composite_patch->flags = 0; + + // work out how much memory we need to allocate for this patch's data + pixelDataSize = (composite_patch->width * composite_patch->height + 4) & ~3; + columnsDataSize = sizeof(rcolumn_t) * composite_patch->width; + + // count the number of posts in each column + countsInColumn = (count_t *)calloc(sizeof(count_t), composite_patch->width); + numPostsTotal = 0; + + for (i=0; ipatchcount; i++) { + texpatch = &texture->patches[i]; + patchNum = texpatch->patch; + oldPatch = (const patch_t*)W_CacheLumpNum(patchNum); + + for (x=0; xwidth); x++) { + int tx = texpatch->originx + x; + + if (tx < 0) + continue; + if (tx >= composite_patch->width) + break; + + countsInColumn[tx].patches++; + + oldColumn = (const column_t *)((const byte *)oldPatch + LittleLong(oldPatch->columnofs[x])); + while (oldColumn->topdelta != 0xff) { + countsInColumn[tx].posts++; + numPostsTotal++; + oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4); + } + } + + W_UnlockLumpNum(patchNum); + } + + postsDataSize = numPostsTotal * sizeof(rpost_t); + + // allocate our data chunk + dataSize = pixelDataSize + columnsDataSize + postsDataSize; + composite_patch->data = (unsigned char*)Z_Malloc(dataSize, PU_STATIC, (void **)&composite_patch->data); + memset(composite_patch->data, 0, dataSize); + + // set out pixel, column, and post pointers into our data array + composite_patch->pixels = composite_patch->data; + composite_patch->columns = (rcolumn_t*)((unsigned char*)composite_patch->pixels + pixelDataSize); + composite_patch->posts = (rpost_t*)((unsigned char*)composite_patch->columns + columnsDataSize); + + // sanity check that we've got all the memory allocated we need + assert((((byte*)composite_patch->posts + numPostsTotal*sizeof(rpost_t)) - (byte*)composite_patch->data) == dataSize); + + if (playpal_transparent != 0) + memset(composite_patch->pixels, playpal_transparent, + (composite_patch->width*composite_patch->height)); + + numPostsUsedSoFar = 0; + + for (x=0; xwidth; x++) { + // setup the column's data + composite_patch->columns[x].pixels = composite_patch->pixels + (x*composite_patch->height); + composite_patch->columns[x].numPosts = countsInColumn[x].posts; + composite_patch->columns[x].posts = composite_patch->posts + numPostsUsedSoFar; + numPostsUsedSoFar += countsInColumn[x].posts; + } + + // fill in the pixels, posts, and columns + for (i=0; ipatchcount; i++) { + texpatch = &texture->patches[i]; + patchNum = texpatch->patch; + oldPatch = (const patch_t*)W_CacheLumpNum(patchNum); + + for (x=0; xwidth); x++) { + int top = -1; + int tx = texpatch->originx + x; + + if (tx < 0) + continue; + if (tx >= composite_patch->width) + break; + + oldColumn = (const column_t *)((const byte *)oldPatch + LittleLong(oldPatch->columnofs[x])); + + { + // tiling + int prevColumnIndex = x-1; + int nextColumnIndex = x+1; + while (prevColumnIndex < 0) prevColumnIndex += LittleShort(oldPatch->width); + while (nextColumnIndex >= LittleShort(oldPatch->width)) nextColumnIndex -= LittleShort(oldPatch->width); + oldPrevColumn = (const column_t *)((const byte *)oldPatch + LittleLong(oldPatch->columnofs[prevColumnIndex])); + oldNextColumn = (const column_t *)((const byte *)oldPatch + LittleLong(oldPatch->columnofs[nextColumnIndex])); + } + + while (oldColumn->topdelta != 0xff) { + rpost_t *post = &composite_patch->columns[tx].posts[countsInColumn[tx].posts_used]; + + //e6y: support for DeePsea's true tall patches + if (oldColumn->topdelta <= top) + { + top += oldColumn->topdelta; + } + else + { + top = oldColumn->topdelta; + } + + oldColumnPixelData = (const byte *)oldColumn + 3; + oy = texpatch->originy; + count = oldColumn->length; + // the original renderer had several bugs which we reproduce here + if (countsInColumn[tx].patches > 1) { + // when there are multiple patches, then we need to handle the + // column differently + if (i == 0) { + // draw first patch at original position, it will be partly + // overdrawn below + for (y=0; y= composite_patch->height) + break; + StorePixel(composite_patch, tx, ty, oldColumnPixelData[y]); + } + } + // do the buggy clipping + if ((oy + top) < 0) { + count += oy; + oy = 0; + } + } else { + // with a single patch only negative y origins are wrong + oy = 0; + } + // set up the post's data + post->topdelta = top + oy; + post->length = count; + if ((post->topdelta + post->length) > composite_patch->height) { + if (post->topdelta > composite_patch->height) + post->length = 0; + else + post->length = composite_patch->height - post->topdelta; + } + if (post->topdelta < 0) { + if ((post->topdelta + post->length) <= 0) + post->length = 0; + else + post->length -= post->topdelta; + post->topdelta = 0; + } + post->slope = 0; + + edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, top); + if (edgeSlope == 1) post->slope |= RDRAW_EDGESLOPE_TOP_UP; + else if (edgeSlope == -1) post->slope |= RDRAW_EDGESLOPE_TOP_DOWN; + + edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, top+count); + if (edgeSlope == 1) post->slope |= RDRAW_EDGESLOPE_BOT_UP; + else if (edgeSlope == -1) post->slope |= RDRAW_EDGESLOPE_BOT_DOWN; + + // fill in the post's pixels + for (y=0; y= composite_patch->height) + break; + StorePixel(composite_patch, tx, ty, oldColumnPixelData[y]); + } + + oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4); + countsInColumn[tx].posts_used++; + assert(countsInColumn[tx].posts_used <= countsInColumn[tx].posts); + } + } + + W_UnlockLumpNum(patchNum); + } + + for (x=0; xwidth; x++) { + rcolumn_t *column; + + if (countsInColumn[x].patches <= 1) + continue; + + // cleanup posts on multipatch columns + column = &composite_patch->columns[x]; + + i = 0; + while (i<(column->numPosts-1)) { + rpost_t *post1 = &column->posts[i]; + rpost_t *post2 = &column->posts[i+1]; + int length; + + if ((post2->topdelta - post1->topdelta) < 0) + switchPosts(post1, post2); + + if ((post1->topdelta + post1->length) >= post2->topdelta) { + length = (post1->length + post2->length) - ((post1->topdelta + post1->length) - post2->topdelta); + if (post1->length < length) { + post1->slope = post2->slope; + post1->length = length; + } + removePostFromColumn(column, i+1); + i = 0; + continue; + } + i++; + } + } + + FillEmptySpace(composite_patch); + + free(countsInColumn); +} + +//--------------------------------------------------------------------------- +const rpatch_t *R_CachePatchNum(int id) { + const int locks = 1; + + if (!patches) + I_Error("R_CachePatchNum: Patches not initialized"); + +#ifdef RANGECHECK + if (id >= numlumps) + I_Error("createPatch: %i >= numlumps", id); +#endif + + if (!patches[id].data) + createPatch(id); + + /* cph - if wasn't locked but now is, tell z_zone to hold it */ + if (!patches[id].locks && locks) { + Z_ChangeTag(patches[id].data,PU_STATIC); +#ifdef TIMEDIAG + patches[id].locktic = gametic; +#endif + } + patches[id].locks += locks; + +#ifdef SIMPLECHECKS + if (!((patches[id].locks+1) & 0xf)) + lprintf(LO_DEBUG, "R_CachePatchNum: High lock on %.8s (%d)\n", + lumpinfo[id].name, patches[id].locks); +#endif + + return &patches[id]; +} + +void R_UnlockPatchNum(int id) +{ + const int unlocks = 1; +#ifdef SIMPLECHECKS + if ((signed short)patches[id].locks < unlocks) + lprintf(LO_DEBUG, "R_UnlockPatchNum: Excess unlocks on %8s (%d-%d)\n", + lumpinfo[id].name, patches[id].locks, unlocks); +#endif + patches[id].locks -= unlocks; + /* cph - Note: must only tell z_zone to make purgeable if currently locked, + * else it might already have been purged + */ + if (unlocks && !patches[id].locks) + Z_ChangeTag(patches[id].data, PU_CACHE); +} + +//--------------------------------------------------------------------------- +const rpatch_t *R_CacheTextureCompositePatchNum(int id) { + const int locks = 1; + + if (!texture_composites) + I_Error("R_CacheTextureCompositePatchNum: Composite patches not initialized"); + +#ifdef RANGECHECK + if (id >= numtextures) + I_Error("createTextureCompositePatch: %i >= numtextures", id); +#endif + + if (!texture_composites[id].data) + createTextureCompositePatch(id); + + /* cph - if wasn't locked but now is, tell z_zone to hold it */ + if (!texture_composites[id].locks && locks) { + Z_ChangeTag(texture_composites[id].data,PU_STATIC); +#ifdef TIMEDIAG + texture_composites[id].locktic = gametic; +#endif + } + texture_composites[id].locks += locks; + +#ifdef SIMPLECHECKS + if (!((texture_composites[id].locks+1) & 0xf)) + lprintf(LO_DEBUG, "R_CacheTextureCompositePatchNum: High lock on %.8s (%d)\n", + textures[id]->name, texture_composites[id].locks); +#endif + + return &texture_composites[id]; + +} + +void R_UnlockTextureCompositePatchNum(int id) +{ + const int unlocks = 1; +#ifdef SIMPLECHECKS + if ((signed short)texture_composites[id].locks < unlocks) + lprintf(LO_DEBUG, "R_UnlockTextureCompositePatchNum: Excess unlocks on %8s (%d-%d)\n", + textures[id]->name, texture_composites[id].locks, unlocks); +#endif + texture_composites[id].locks -= unlocks; + /* cph - Note: must only tell z_zone to make purgeable if currently locked, + * else it might already have been purged + */ + if (unlocks && !texture_composites[id].locks) + Z_ChangeTag(texture_composites[id].data, PU_CACHE); +} + +//--------------------------------------------------------------------------- +const rcolumn_t *R_GetPatchColumnWrapped(const rpatch_t *patch, int columnIndex) { + while (columnIndex < 0) columnIndex += patch->width; + columnIndex %= patch->width; + return &patch->columns[columnIndex]; +} + +//--------------------------------------------------------------------------- +const rcolumn_t *R_GetPatchColumnClamped(const rpatch_t *patch, int columnIndex) { + if (columnIndex < 0) columnIndex = 0; + if (columnIndex >= patch->width) columnIndex = patch->width-1; + return &patch->columns[columnIndex]; +} + +//--------------------------------------------------------------------------- +const rcolumn_t *R_GetPatchColumn(const rpatch_t *patch, int columnIndex) { + if (patch->flags&PATCH_ISNOTTILEABLE) return R_GetPatchColumnClamped(patch, columnIndex); + else return R_GetPatchColumnWrapped(patch, columnIndex); +} + diff --git a/src/r_patch.h b/src/r_patch.h new file mode 100644 index 0000000..0356793 --- /dev/null +++ b/src/r_patch.h @@ -0,0 +1,117 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef R_PATCH_H +#define R_PATCH_H + +// Used to specify the sloping of the top and bottom of a column post +typedef enum { + RDRAW_EDGESLOPE_TOP_UP = (1<<0), + RDRAW_EDGESLOPE_TOP_DOWN = (1<<1), + RDRAW_EDGESLOPE_BOT_UP = (1<<2), + RDRAW_EDGESLOPE_BOT_DOWN = (1<<3), + RDRAW_EDGESLOPE_TOP_MASK = 0x3, + RDRAW_EDGESLOPE_BOT_MASK = 0xc, +} edgeslope_t; + +//e6y +typedef enum { + PATCH_ISNOTTILEABLE = 0x00000001, + PATCH_REPEAT = 0x00000002, + PATCH_HASHOLES = 0x00000004, +} rpatch_flag_t; + +typedef struct { + int topdelta; + int length; + edgeslope_t slope; +} rpost_t; + +typedef struct { + int numPosts; + rpost_t *posts; + unsigned char *pixels; +} rcolumn_t; + +typedef struct { + int width; + int height; + unsigned widthmask; + + int leftoffset; + int topoffset; + + // this is the single malloc'ed/free'd array + // for this patch + unsigned char *data; + + // these are pointers into the data array + unsigned char *pixels; + rcolumn_t *columns; + rpost_t *posts; + +#ifdef TIMEDIAG + int locktic; +#endif + unsigned int locks; + unsigned int flags;//e6y +} rpatch_t; + + +const rpatch_t *R_CachePatchNum(int id); +void R_UnlockPatchNum(int id); +#define R_CachePatchName(name) R_CachePatchNum(W_GetNumForName(name)) +#define R_UnlockPatchName(name) R_UnlockPatchNum(W_GetNumForName(name)) + +const rpatch_t *R_CacheTextureCompositePatchNum(int id); +void R_UnlockTextureCompositePatchNum(int id); + + +// Size query funcs +int R_NumPatchWidth(int lump) ; +int R_NumPatchHeight(int lump); +#define R_NamePatchWidth(name) R_NumPatchWidth(W_GetNumForName(name)) +#define R_NamePatchHeight(name) R_NumPatchHeight(W_GetNumForName(name)) + + +const rcolumn_t *R_GetPatchColumnWrapped(const rpatch_t *patch, int columnIndex); +const rcolumn_t *R_GetPatchColumnClamped(const rpatch_t *patch, int columnIndex); + + +// returns R_GetPatchColumnWrapped for square, non-holed textures +// and R_GetPatchColumnClamped otherwise +const rcolumn_t *R_GetPatchColumn(const rpatch_t *patch, int columnIndex); + + +void R_InitPatches(); +void R_FlushAllPatches(); + +#endif diff --git a/src/r_plane.c b/src/r_plane.c new file mode 100644 index 0000000..0b101f6 --- /dev/null +++ b/src/r_plane.c @@ -0,0 +1,535 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * 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. + * + * MAXVISPLANES is no longer a limit on the number of visplanes, + * but a limit on the number of hash slots; larger numbers mean + * better performance usually but after a point they are wasted, + * and memory and time overheads creep in. + * + * For more information on visplanes, see: + * + * http://classicgaming.com/doom/editing/ + * + * Lee Killough + * + *-----------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "z_zone.h" /* memory allocation wrappers -- killough */ + +#include "doomstat.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_draw.h" +#include "r_things.h" +#include "r_sky.h" +#include "r_plane.h" +#include "r_main.h" +#include "v_video.h" +#include "lprintf.h" + +#define MAXVISPLANES 128 /* must be a power of 2 */ + +static visplane_t *visplanes[MAXVISPLANES]; // killough +static visplane_t *freetail; // killough +static visplane_t **freehead = &freetail; // killough +visplane_t *floorplane, *ceilingplane; + +// killough -- hash function for visplanes +// Empirically verified to be fairly uniform: + +#define visplane_hash(picnum,lightlevel,height) \ + ((unsigned)((picnum)*3+(lightlevel)+(height)*7) & (MAXVISPLANES-1)) + +size_t maxopenings; +int *openings,*lastopening; // dropoff overflow + +// Clip values are the solid pixel bounding the range. +// floorclip starts out SCREENHEIGHT +// ceilingclip starts out -1 + +// dropoff overflow +// e6y: resolution limitation is removed +int *floorclip = NULL; +int *ceilingclip = NULL; + +// spanstart holds the start of a plane span; initialized to 0 at start + +// e6y: resolution limitation is removed +static int *spanstart = NULL; // killough 2/8/98 + +// +// texture mapping +// + +static const lighttable_t **planezlight; +static fixed_t planeheight; + +// killough 2/8/98: make variables static + +static fixed_t basexscale, baseyscale; +static fixed_t *cachedheight = NULL; +static fixed_t xoffs,yoffs; // killough 2/28/98: flat offsets + +// e6y: resolution limitation is removed +fixed_t *yslope = NULL; +fixed_t *distscale = NULL; + +void R_InitPlanesRes(void) +{ + if (floorclip) free(floorclip); + if (ceilingclip) free(ceilingclip); + if (spanstart) free(spanstart); + + if (cachedheight) free(cachedheight); + + if (yslope) free(yslope); + if (distscale) free(distscale); + + floorclip = calloc(1, SCREENWIDTH * sizeof(*floorclip)); + ceilingclip = calloc(1, SCREENWIDTH * sizeof(*ceilingclip)); + spanstart = calloc(1, SCREENHEIGHT * sizeof(*spanstart)); + + cachedheight = calloc(1, SCREENHEIGHT * sizeof(*cachedheight)); + + yslope = calloc(1, SCREENHEIGHT * sizeof(*yslope)); + distscale = calloc(1, SCREENWIDTH * sizeof(*distscale)); +} + +void R_InitVisplanesRes(void) +{ + int i; + + freetail = NULL; + freehead = &freetail; + + for (i = 0; i < MAXVISPLANES; i++) + { + visplanes[i] = 0; + } +} + +// +// R_InitPlanes +// Only at game startup. +// +void R_InitPlanes (void) +{ +} + +// +// R_MapPlane +// +// Uses global vars: +// planeheight +// dsvars.source +// basexscale +// baseyscale +// viewx +// viewy +// xoffs +// yoffs +// +// BASIC PRIMITIVE +// + +static void R_MapPlane(int y, int x1, int x2, draw_span_vars_t *dsvars) +{ + int_64_t den; + fixed_t distance; + unsigned index; + +#ifdef RANGECHECK + if (x2 < x1 || x1<0 || x2>=viewwidth || (unsigned)y>(unsigned)viewheight) + I_Error ("R_MapPlane: %i, %i at %i",x1,x2,y); +#endif + + // [RH]Instead of using the xtoviewangle array, I calculated the fractional values + // at the middle of the screen, then used the calculated ds_xstep and ds_ystep + // to step from those to the proper texture coordinate to start drawing at. + // That way, the texture coordinate is always calculated by its position + // on the screen and not by its position relative to the edge of the visplane. + // + // Visplanes with the same texture now match up far better than before. + // + // See cchest2.wad/map02/room with sector #265 + if (centery == y) + return; + den = (int_64_t)FRACUNIT * FRACUNIT * D_abs(centery - y); + distance = FixedMul (planeheight, yslope[y]); + + dsvars->xstep = (fixed_t)((int_64_t)viewsin * planeheight * viewfocratio / den); + dsvars->ystep = (fixed_t)((int_64_t)viewcos * planeheight * viewfocratio / den); + + // killough 2/28/98: Add offsets + dsvars->xfrac = viewx + xoffs + FixedMul(viewcos, distance) + (x1 - centerx) * dsvars->xstep; + dsvars->yfrac = -viewy + yoffs - FixedMul(viewsin, distance) + (x1 - centerx) * dsvars->ystep; + + if (drawvars.filterfloor == RDRAW_FILTER_LINEAR) { + dsvars->xfrac -= (FRACUNIT>>1); + dsvars->yfrac -= (FRACUNIT>>1); + } + + if (!(dsvars->colormap = fixedcolormap)) + { + dsvars->z = distance; + index = distance >> LIGHTZSHIFT; + if (index >= MAXLIGHTZ ) + index = MAXLIGHTZ-1; + dsvars->colormap = planezlight[index]; + dsvars->nextcolormap = planezlight[index+1 >= MAXLIGHTZ ? MAXLIGHTZ-1 : index+1]; + } + else + { + dsvars->z = 0; + } + + dsvars->y = y; + dsvars->x1 = x1; + dsvars->x2 = x2; + + if (V_GetMode() != VID_MODEGL) + R_DrawSpan(dsvars); +} + +// +// R_ClearPlanes +// At begining of frame. +// + +void R_ClearPlanes(void) +{ + int i; + + // opening / clipping determination + for (i=0 ; inext; + + lastopening = openings; + + // texture calculation + memset (cachedheight, 0, SCREENHEIGHT * sizeof(*cachedheight)); + + // scale will be unit scale at SCREENWIDTH/2 distance + basexscale = FixedDiv (viewsin,projection); + baseyscale = FixedDiv (viewcos,projection); +} + +// New function, by Lee Killough + +static visplane_t *new_visplane(unsigned hash) +{ + visplane_t *check = freetail; + if (!check) + { + // e6y: resolution limitation is removed + check = calloc(1, sizeof(*check) + sizeof(*check->top) * (SCREENWIDTH * 2)); + check->bottom = &check->top[SCREENWIDTH + 2]; + } + else + if (!(freetail = freetail->next)) + freehead = &freetail; + check->next = visplanes[hash]; + visplanes[hash] = check; + return check; +} + +/* + * R_DupPlane + * + * cph 2003/04/18 - create duplicate of existing visplane and set initial range + */ +visplane_t *R_DupPlane(const visplane_t *pl, int start, int stop) +{ + int i; + unsigned hash = visplane_hash(pl->picnum, pl->lightlevel, pl->height); + visplane_t *new_pl = new_visplane(hash); + + new_pl->height = pl->height; + new_pl->picnum = pl->picnum; + new_pl->lightlevel = pl->lightlevel; + new_pl->xoffs = pl->xoffs; // killough 2/28/98 + new_pl->yoffs = pl->yoffs; + new_pl->minx = start; + new_pl->maxx = stop; + for (i = 0; i != SCREENWIDTH; i++) + new_pl->top[i] = SHRT_MAX; + return new_pl; +} +// +// R_FindPlane +// +// killough 2/28/98: Add offsets + +visplane_t *R_FindPlane(fixed_t height, int picnum, int lightlevel, + fixed_t xoffs, fixed_t yoffs) +{ + visplane_t *check; + unsigned hash; // killough + + if (picnum == skyflatnum || picnum & PL_SKYFLAT) + height = lightlevel = 0; // killough 7/19/98: most skies map together + + // New visplane algorithm uses hash table -- killough + hash = visplane_hash(picnum,lightlevel,height); + + for (check=visplanes[hash]; check; check=check->next) // killough + if (height == check->height && + picnum == check->picnum && + lightlevel == check->lightlevel && + xoffs == check->xoffs && // killough 2/28/98: Add offset checks + yoffs == check->yoffs) + return check; + + check = new_visplane(hash); // killough + + check->height = height; + check->picnum = picnum; + check->lightlevel = lightlevel; + check->xoffs = xoffs; // killough 2/28/98: Save offsets + check->yoffs = yoffs; +#ifdef GL_DOOM + if (V_GetMode() != VID_MODEGL) +#endif + { + int i; + check->minx = viewwidth; // Was SCREENWIDTH -- killough 11/98 + check->maxx = -1; + + for (i = 0; i != SCREENWIDTH; i++) + check->top[i] = SHRT_MAX; + } + + return check; +} + +// +// R_CheckPlane +// +visplane_t *R_CheckPlane(visplane_t *pl, int start, int stop) +{ + int intrl, intrh, unionl, unionh, 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 && pl->top[x] == SHRT_MAX; x++) // dropoff overflow + ; + + if (x > intrh) { /* Can use existing plane; extend range */ + pl->minx = unionl; pl->maxx = unionh; + return pl; + } else /* Cannot use existing plane; create a new one */ + return R_DupPlane(pl,start,stop); +} + +// +// R_MakeSpans +// + +static void R_MakeSpans(int x, unsigned int t1, unsigned int b1, + unsigned int t2, unsigned int b2, + draw_span_vars_t *dsvars) +{ + for (; t1 < t2 && t1 <= b1; t1++) + R_MapPlane(t1, spanstart[t1], x-1, dsvars); + for (; b1 > b2 && b1 >= t1; b1--) + R_MapPlane(b1, spanstart[b1] ,x-1, dsvars); + while (t2 < t1 && t2 <= b2) + spanstart[t2++] = x; + while (b2 > b1 && b2 >= t2) + spanstart[b2--] = x; +} + +// New function, by Lee Killough + +static void R_DoDrawPlane(visplane_t *pl) +{ + register int x; + draw_column_vars_t dcvars; + R_DrawColumn_f colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_STANDARD, drawvars.filterwall, drawvars.filterz); + + R_SetDefaultDrawColumnVars(&dcvars); + + if (pl->minx <= pl->maxx) { + if (pl->picnum == skyflatnum || pl->picnum & PL_SKYFLAT) { // sky flat + int texture; + const rpatch_t *tex_patch; + angle_t an, flip; + + // killough 10/98: allow skies to come from sidedefs. + // Allows scrolling and/or animated skies, as well as + // arbitrary multiple skies per level without having + // to use info lumps. + + an = viewangle; + + if (pl->picnum & PL_SKYFLAT) + { + // Sky Linedef + const line_t *l = &lines[pl->picnum & ~PL_SKYFLAT]; + + // Sky transferred from first sidedef + const side_t *s = *l->sidenum + sides; + + // Texture comes from upper texture of reference sidedef + texture = texturetranslation[s->toptexture]; + + // Horizontal offset is turned into an angle offset, + // to allow sky rotation as well as careful positioning. + // However, the offset is scaled very small, so that it + // allows a long-period of sky rotation. + + an += s->textureoffset; + + // Vertical offset allows careful sky positioning. + + dcvars.texturemid = s->rowoffset - 28*FRACUNIT; + + // We sometimes flip the picture horizontally. + // + // Doom always flipped the picture, so we make it optional, + // to make it easier to use the new feature, while to still + // allow old sky textures to be used. + + flip = l->special==272 ? 0u : ~0u; + + if (skystretch) + { + int skyheight = textureheight[texture]>>FRACBITS; + dcvars.texturemid = (int)((int_64_t)dcvars.texturemid * skyheight / SKYSTRETCH_HEIGHT); + } + } + else + { // Normal Doom sky, only one allowed per level + dcvars.texturemid = skytexturemid; // Default y-offset + texture = skytexture; // Default texture + flip = 0; // Doom flips it + } + + /* Sky is always drawn full bright, i.e. colormaps[0] is used. + * Because of this hack, sky is not affected by INVUL inverse mapping. + * Until Boom fixed this. Compat option added in MBF. */ + + if (comp[comp_skymap] || !(dcvars.colormap = fixedcolormap)) + dcvars.colormap = fullcolormap; // killough 3/20/98 + + dcvars.nextcolormap = dcvars.colormap; // for filtering -- POPE + + //dcvars.texturemid = skytexturemid; + dcvars.texheight = textureheight[texture]>>FRACBITS; // killough + + // proff 09/21/98: Changed for high-res + + // e6y + // disable sky texture scaling if status bar is used + // old code: dcvars.iscale = FRACUNIT*200/viewheight; + dcvars.iscale = skyiscale; + + tex_patch = R_CacheTextureCompositePatchNum(texture); + + // killough 10/98: Use sky scrolling offset, and possibly flip picture + for (x = pl->minx; (dcvars.x = x) <= pl->maxx; x++) + if ((dcvars.yl = pl->top[x]) != SHRT_MAX && dcvars.yl <= (dcvars.yh = pl->bottom[x])) // dropoff overflow + { + dcvars.source = R_GetTextureColumn(tex_patch, ((an + xtoviewangle[x])^flip) >> ANGLETOSKYSHIFT); + dcvars.prevsource = R_GetTextureColumn(tex_patch, ((an + xtoviewangle[x-1])^flip) >> ANGLETOSKYSHIFT); + dcvars.nextsource = R_GetTextureColumn(tex_patch, ((an + xtoviewangle[x+1])^flip) >> ANGLETOSKYSHIFT); + colfunc(&dcvars); + } + + R_UnlockTextureCompositePatchNum(texture); + + } else { // regular flat + + int stop, light; + draw_span_vars_t dsvars; + + dsvars.source = W_CacheLumpNum(firstflat + flattranslation[pl->picnum]); + + xoffs = pl->xoffs; // killough 2/28/98: Add offsets + yoffs = pl->yoffs; + planeheight = D_abs(pl->height-viewz); + + // SoM 10/19/02: deep water colormap fix + if(fixedcolormap) + light = (255 >> LIGHTSEGSHIFT); + else + light = (pl->lightlevel >> LIGHTSEGSHIFT) + (extralight * LIGHTBRIGHT); + + if(light >= LIGHTLEVELS) + light = LIGHTLEVELS-1; + + if(light < 0) + light = 0; + + stop = pl->maxx + 1; + planezlight = zlight[light]; + pl->top[pl->minx-1] = pl->top[stop] = SHRT_MAX; // dropoff overflow + + for (x = pl->minx ; x <= stop ; x++) + R_MakeSpans(x,pl->top[x-1],pl->bottom[x-1], + pl->top[x],pl->bottom[x], &dsvars); + + W_UnlockLumpNum(firstflat + flattranslation[pl->picnum]); + } + } +} + +// +// RDrawPlanes +// At the end of each frame. +// + +void R_DrawPlanes (void) +{ + visplane_t *pl; + int i; + for (i=0;inext, rendered_visplanes++) + R_DoDrawPlane(pl); +} diff --git a/src/r_plane.h b/src/r_plane.h new file mode 100644 index 0000000..55302c5 --- /dev/null +++ b/src/r_plane.h @@ -0,0 +1,70 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh, visplane stuff (floor, ceilings). + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_PLANE__ +#define __R_PLANE__ + +#include "r_data.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +/* killough 10/98: special mask indicates sky flat comes from sidedef */ +#define PL_SKYFLAT (0x80000000) + +/* Visplane related. */ +extern int *lastopening; // dropoff overflow + +// e6y: resolution limitation is removed +extern int *floorclip, *ceilingclip; // dropoff overflow +extern fixed_t *yslope, *distscale; + +void R_InitVisplanesRes(void); +void R_InitPlanesRes(void); +void R_InitPlanes(void); +void R_ClearPlanes(void); +void R_DrawPlanes (void); + +visplane_t *R_FindPlane( + fixed_t height, + int picnum, + int lightlevel, + fixed_t xoffs, /* killough 2/28/98: add x-y offsets */ + fixed_t yoffs + ); + +visplane_t *R_CheckPlane(visplane_t *pl, int start, int stop); +visplane_t *R_DupPlane(const visplane_t *pl, int start, int stop); + +#endif diff --git a/src/r_segs.c b/src/r_segs.c new file mode 100644 index 0000000..cff502c --- /dev/null +++ b/src/r_segs.c @@ -0,0 +1,1027 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * All the clipping: columns, horizontal spans, sky columns. + * + *-----------------------------------------------------------------------------*/ +// +// 4/25/98, 5/2/98 killough: reformatted, beautified + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "SDL.h" + +#include "doomstat.h" +#include "r_main.h" +#include "r_bsp.h" +#include "r_segs.h" +#include "r_plane.h" +#include "r_things.h" +#include "r_draw.h" +#include "w_wad.h" +#include "v_video.h" +#include "lprintf.h" + +// OPTIMIZE: closed two sided lines as single sided + +// killough 1/6/98: replaced globals with statics where appropriate + +// True if any of the segs textures might be visible. +static dboolean segtextured; +static dboolean markfloor; // False if the back side is the same plane. +static dboolean markceiling; +static dboolean maskedtexture; +static int toptexture; +static int bottomtexture; +static int midtexture; + +static fixed_t toptexheight, midtexheight, bottomtexheight; // cph + +angle_t rw_normalangle; // angle to line origin +int rw_angle1; +fixed_t rw_distance; +const lighttable_t **walllights; +const lighttable_t **walllightsnext; + +// +// regular wall +// +static int rw_x; +static int rw_stopx; +static angle_t rw_centerangle; +static fixed_t rw_offset; +static fixed_t rw_scale; +static fixed_t rw_scalestep; +static fixed_t rw_midtexturemid; +static fixed_t rw_toptexturemid; +static fixed_t rw_bottomtexturemid; +static int rw_lightlevel; +static int worldtop; +static int worldbottom; +static int worldhigh; +static int worldlow; +static int_64_t pixhigh; // R_WiggleFix +static int_64_t pixlow; // R_WiggleFix +static fixed_t pixhighstep; +static fixed_t pixlowstep; +static int_64_t topfrac; // R_WiggleFix +static fixed_t topstep; +static int_64_t bottomfrac; // R_WiggleFix +static fixed_t bottomstep; +static int *maskedtexturecol; // dropoff overflow + +static int max_rwscale = 64 * FRACUNIT; +static int HEIGHTBITS = 12; +static int HEIGHTUNIT = (1 << 12); +static int invhgtbits = 4; + +// +// R_FixWiggle() +// Dynamic wall/texture rescaler, AKA "WiggleHack II" +// by Kurt "kb1" Baumgardner ("kb") +// +// [kb] When the rendered view is positioned, such that the viewer is +// looking almost parallel down a wall, the result of the scale +// calculation in R_ScaleFromGlobalAngle becomes very large. And, the +// taller the wall, the larger that value becomes. If these large +// values were used as-is, subsequent calculations would overflow +// and crash the program. +// +// Therefore, vanilla Doom clamps this scale calculation, preventing it +// from becoming larger than 0x400000 (64*FRACUNIT). This number was +// chosen carefully, to allow reasonably-tight angles, with reasonably +// tall sectors to be rendered, within the limits of the fixed-point +// math system being used. When the scale gets clamped, Doom cannot +// properly render the wall, causing an undesirable wall-bending +// effect that I call "floor wiggle". +// +// Modern source ports offer higher video resolutions, which worsens +// the issue. And, Doom is simply not adjusted for the taller walls +// found in many PWADs. +// +// WiggleHack II attempts to correct these issues, by dynamically +// adjusting the fixed-point math, and the maximum scale clamp, +// on a wall-by-wall basis. This has 2 effects: +// +// 1. Floor wiggle is greatly reduced and/or eliminated. +// 2. Overflow is not longer possible, even in levels with maximum +// height sectors. +// +// It is not perfect across all situations. Some floor wiggle can be +// seen, and some texture strips may be slight misaligned in extreme +// cases. These effects cannot be corrected without increasing the +// precision of various renderer variables, and, possibly, suffering +// a performance penalty. +// + +void R_FixWiggle(sector_t *sec) +{ + static int lastheight = 0; + + static const struct + { + int clamp; + int heightbits; + } scale_values[9] = { + {2048 * FRACUNIT, 12}, {1024 * FRACUNIT, 12}, {1024 * FRACUNIT, 11}, + { 512 * FRACUNIT, 11}, { 512 * FRACUNIT, 10}, { 256 * FRACUNIT, 10}, + { 256 * FRACUNIT, 9}, { 128 * FRACUNIT, 9}, { 64 * FRACUNIT, 9}, + }; + + int height = (sec->ceilingheight - sec->floorheight) >> FRACBITS; + + // disallow negative heights, force cache initialization + if (height < 1) + height = 1; + + // early out? + if (height != lastheight) + { + lastheight = height; + + // initialize, or handle moving sector + if (height != sec->cachedheight) + { + frontsector->cachedheight = height; + frontsector->scaleindex = 0; + height >>= 7; + // calculate adjustment + while ((height >>= 1)) + frontsector->scaleindex++; + } + + // fine-tune renderer for this wall + max_rwscale = scale_values[frontsector->scaleindex].clamp; + HEIGHTBITS = scale_values[frontsector->scaleindex].heightbits; + HEIGHTUNIT = 1 << HEIGHTBITS; + invhgtbits = 16 - HEIGHTBITS; + } +} + +// +// R_ScaleFromGlobalAngle +// Returns the texture mapping scale +// for the current line (horizontal span) +// at the given angle. +// rw_distance must be calculated first. +// +// killough 5/2/98: reformatted, cleaned up +// CPhipps - moved here from r_main.c + +static fixed_t R_ScaleFromGlobalAngle(angle_t visangle) +{ + int anglea = ANG90 + (visangle - viewangle); + int angleb = ANG90 + (visangle - rw_normalangle); + int den = FixedMul(rw_distance, finesine[anglea >> ANGLETOFINESHIFT]); + // proff 11/06/98: Changed for high-res + fixed_t num = FixedMul(projectiony, finesine[angleb >> ANGLETOFINESHIFT]); + fixed_t scale; + + if (den > (num >> 16)) + { + scale = FixedDiv(num, den); + + // [kb] use R_WiggleFix clamp + if (scale > max_rwscale) + scale = max_rwscale; + else if (scale < 256) + scale = 256; + } + else + scale = max_rwscale; + + return scale; +} + +const lighttable_t** GetLightTable(int lightlevel) +{ + int lightnum = (lightlevel >> LIGHTSEGSHIFT) + (extralight * LIGHTBRIGHT); + + /* cph - ...what is this for? adding contrast to rooms? + * It looks crap in outdoor areas */ + if (fake_contrast && curline) + { + if (curline->v1->y == curline->v2->y) + { + lightnum -= LIGHTBRIGHT; + } + else + { + if (curline->v1->x == curline->v2->x) + { + lightnum += LIGHTBRIGHT; + } + } + } + + return scalelight[BETWEEN(0, LIGHTLEVELS - 1, lightnum)]; +} + +// +// R_RenderMaskedSegRange +// + +void R_RenderMaskedSegRange(drawseg_t *ds, int x1, int x2) +{ + int texnum; + sector_t tempsec; // killough 4/13/98 + const rpatch_t *patch; + R_DrawColumn_f colfunc; + draw_column_vars_t dcvars; + angle_t angle; + + R_SetDefaultDrawColumnVars(&dcvars); + + // Calculate light table. + // Use different light tables + // for horizontal / vertical / diagonal. Diagonal? + + curline = ds->curline; // OPTIMIZE: get rid of LIGHTSEGSHIFT globally + + // killough 4/11/98: draw translucent 2s normal textures + + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_STANDARD, drawvars.filterwall, drawvars.filterz); + if (curline->linedef->tranlump >= 0 && general_translucency) + { + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_TRANSLUCENT, drawvars.filterwall, drawvars.filterz); + tranmap = main_tranmap; + if (curline->linedef->tranlump > 0) + tranmap = W_CacheLumpNum(curline->linedef->tranlump-1); + } + // killough 4/11/98: end translucent 2s normal code + + frontsector = curline->frontsector; + backsector = curline->backsector; + + // cph 2001/11/25 - middle textures did not animate in v1.2 + texnum = curline->sidedef->midtexture; + if (!comp[comp_maskedanim]) + texnum = texturetranslation[texnum]; + + // killough 4/13/98: get correct lightlevel for 2s normal textures + rw_lightlevel = R_FakeFlat(frontsector, &tempsec, NULL, NULL, false) ->lightlevel; + walllights = GetLightTable(rw_lightlevel); + walllightsnext = GetLightTable(rw_lightlevel + 1); + + 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) + { + dcvars.texturemid = frontsector->floorheight > backsector->floorheight + ? frontsector->floorheight : backsector->floorheight; + dcvars.texturemid = dcvars.texturemid + textureheight[texnum] - viewz; + } + else + { + dcvars.texturemid =frontsector->ceilingheightceilingheight + ? frontsector->ceilingheight : backsector->ceilingheight; + dcvars.texturemid = dcvars.texturemid - viewz; + } + + dcvars.texturemid += curline->sidedef->rowoffset; + + if (fixedcolormap) { + dcvars.colormap = fixedcolormap; + dcvars.nextcolormap = dcvars.colormap; // for filtering -- POPE + } + + patch = R_CacheTextureCompositePatchNum(texnum); + + // draw the columns + for (dcvars.x = x1 ; dcvars.x <= x2 ; dcvars.x++, spryscale += rw_scalestep) + if (maskedtexturecol[dcvars.x] != INT_MAX) // dropoff overflow + { + // calculate texture offset - POPE + angle = (ds->rw_centerangle + xtoviewangle[dcvars.x]) >> ANGLETOFINESHIFT; + dcvars.texu = ds->rw_offset - FixedMul(finetangent[angle], ds->rw_distance); + if (drawvars.filterwall == RDRAW_FILTER_LINEAR) + dcvars.texu -= (FRACUNIT>>1); + + if (!fixedcolormap) + dcvars.z = spryscale; // for filtering -- POPE + + if (!fixedcolormap) + { + int index = (int)(((int_64_t)spryscale * 160 / wide_centerx) >> LIGHTSCALESHIFT); + if (index >= MAXLIGHTSCALE) + index = MAXLIGHTSCALE - 1; + + dcvars.colormap = walllights[index]; + dcvars.nextcolormap = walllightsnext[index]; + } + else + { + dcvars.colormap = fixedcolormap; + dcvars.nextcolormap = fixedcolormap; + } + + // killough 3/2/98: + // + // This calculation used to overflow and cause crashes in Doom: + // + // sprtopscreen = centeryfrac - FixedMul(dcvars.texturemid, spryscale); + // + // This code fixes it, by using double-precision intermediate + // arithmetic and by skipping the drawing of 2s normals whose + // mapping to screen coordinates is totally out of range: + + { + int_64_t t = ((int_64_t) centeryfrac << FRACBITS) - + (int_64_t) dcvars.texturemid * spryscale; + if (t + (int_64_t) textureheight[texnum] * spryscale < 0 || + t > (int_64_t) SCREENHEIGHT << FRACBITS*2) + continue; // skip if the texture is out of screen's range + sprtopscreen = (int_64_t)(t >> FRACBITS); // R_WiggleFix + } + + dcvars.iscale = 0xffffffffu / (unsigned) spryscale; + + // killough 1/25/98: here's where Medusa came in, because + // it implicitly assumed that the column was all one patch. + // Originally, Doom did not construct complete columns for + // multipatched textures, so there were no header or trailer + // bytes in the column referred to below, which explains + // the Medusa effect. The fix is to construct true columns + // when forming multipatched textures (see r_data.c). + + // draw the texture + R_DrawMaskedColumn( + patch, + colfunc, + &dcvars, + R_GetPatchColumnWrapped(patch, maskedtexturecol[dcvars.x]), + R_GetPatchColumnWrapped(patch, maskedtexturecol[dcvars.x]-1), + R_GetPatchColumnWrapped(patch, maskedtexturecol[dcvars.x]+1) + ); + + maskedtexturecol[dcvars.x] = INT_MAX; // dropoff overflow + } + + // Except for main_tranmap, mark others purgable at this point + if (curline->linedef->tranlump > 0 && general_translucency) + W_UnlockLumpNum(curline->linedef->tranlump-1); // cph - unlock it + + R_UnlockTextureCompositePatchNum(texnum); + + curline = NULL; /* cph 2001/11/18 - must clear curline now we're done with it, so R_ColourMap doesn't try using it for other things */ +} + +// +// R_RenderSegLoop +// Draws zero, one, or two textures (and possibly a masked texture) for walls. +// Can draw or mark the starting pixel of floor and ceiling textures. +// CALLED: CORE LOOPING ROUTINE. +// + +static int didsolidcol; /* True if at least one column was marked solid */ + +static void R_RenderSegLoop (void) +{ + const rpatch_t *tex_patch; + R_DrawColumn_f colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_STANDARD, drawvars.filterwall, drawvars.filterz); + draw_column_vars_t dcvars; + fixed_t texturecolumn = 0; // shut up compiler warning + + R_SetDefaultDrawColumnVars(&dcvars); + + rendered_segs++; + for ( ; rw_x < rw_stopx ; rw_x++) + { + + // mark floor / ceiling areas + + int yh = (int)(bottomfrac>>HEIGHTBITS); + int yl = (int)((topfrac+HEIGHTUNIT-1)>>HEIGHTBITS); + + // no space above wall? + int bottom,top = ceilingclip[rw_x]+1; + + if (yl < top) + yl = top; + + if (markceiling) + { + 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; + } + // SoM: this should be set here + ceilingclip[rw_x] = bottom; + } + +// yh = bottomfrac>>HEIGHTBITS; + + bottom = floorclip[rw_x]-1; + if (yh > bottom) + yh = bottom; + + if (markfloor) + { + + top = yh < ceilingclip[rw_x] ? ceilingclip[rw_x] : yh; + + if (++top <= bottom) + { + floorplane->top[rw_x] = top; + floorplane->bottom[rw_x] = bottom; + } + // SoM: This should be set here to prevent overdraw + floorclip[rw_x] = top; + } + + // texturecolumn and lighting are independent of wall tiers + if (segtextured) + { + // calculate texture offset + angle_t angle =(rw_centerangle+xtoviewangle[rw_x])>>ANGLETOFINESHIFT; + + texturecolumn = rw_offset-FixedMul(finetangent[angle],rw_distance); + if (drawvars.filterwall == RDRAW_FILTER_LINEAR) + texturecolumn -= (FRACUNIT>>1); + dcvars.texu = texturecolumn; // for filtering -- POPE + texturecolumn >>= FRACBITS; + + // calculate lighting + if (!fixedcolormap) + { + int index = (int)(((int_64_t)rw_scale * 160 / wide_centerx) >> LIGHTSCALESHIFT); + if (index >= MAXLIGHTSCALE) + index = MAXLIGHTSCALE - 1; + + dcvars.colormap = walllights[index]; + dcvars.nextcolormap = walllightsnext[index]; + } + else + { + dcvars.colormap = fixedcolormap; + dcvars.nextcolormap = fixedcolormap; + } + dcvars.z = rw_scale; // for filtering -- POPE + + dcvars.x = rw_x; + dcvars.iscale = 0xffffffffu / (unsigned)rw_scale; + } + + // draw the wall tiers + if (midtexture) + { + + dcvars.yl = yl; // single sided line + dcvars.yh = yh; + dcvars.texturemid = rw_midtexturemid; + tex_patch = R_CacheTextureCompositePatchNum(midtexture); + dcvars.source = R_GetTextureColumn(tex_patch, texturecolumn); + dcvars.prevsource = R_GetTextureColumn(tex_patch, texturecolumn-1); + dcvars.nextsource = R_GetTextureColumn(tex_patch, texturecolumn+1); + dcvars.texheight = midtexheight; + colfunc(&dcvars); + R_UnlockTextureCompositePatchNum(midtexture); + tex_patch = NULL; + ceilingclip[rw_x] = viewheight; + floorclip[rw_x] = -1; + } + else + { + + // two sided line + if (toptexture) + { + // top wall + int mid = (int)(pixhigh>>HEIGHTBITS); + pixhigh += pixhighstep; + + if (mid >= floorclip[rw_x]) + mid = floorclip[rw_x]-1; + + if (mid >= yl) + { + dcvars.yl = yl; + dcvars.yh = mid; + dcvars.texturemid = rw_toptexturemid; + tex_patch = R_CacheTextureCompositePatchNum(toptexture); + dcvars.source = R_GetTextureColumn(tex_patch,texturecolumn); + dcvars.prevsource = R_GetTextureColumn(tex_patch,texturecolumn-1); + dcvars.nextsource = R_GetTextureColumn(tex_patch,texturecolumn+1); + dcvars.texheight = toptexheight; + colfunc(&dcvars); + R_UnlockTextureCompositePatchNum(toptexture); + tex_patch = NULL; + 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 + { + int mid = (int)((pixlow+HEIGHTUNIT-1)>>HEIGHTBITS); + pixlow += pixlowstep; + + // no space above wall? + if (mid <= ceilingclip[rw_x]) + mid = ceilingclip[rw_x]+1; + + if (mid <= yh) + { + dcvars.yl = mid; + dcvars.yh = yh; + dcvars.texturemid = rw_bottomtexturemid; + tex_patch = R_CacheTextureCompositePatchNum(bottomtexture); + dcvars.source = R_GetTextureColumn(tex_patch, texturecolumn); + dcvars.prevsource = R_GetTextureColumn(tex_patch, texturecolumn-1); + dcvars.nextsource = R_GetTextureColumn(tex_patch, texturecolumn+1); + dcvars.texheight = bottomtexheight; + colfunc(&dcvars); + R_UnlockTextureCompositePatchNum(bottomtexture); + tex_patch = NULL; + floorclip[rw_x] = mid; + } + else + floorclip[rw_x] = yh+1; + } + else // no bottom wall + { + if (markfloor) + floorclip[rw_x] = yh+1; + } + + // cph - if we completely blocked further sight through this column, + // add this info to the solid columns array for r_bsp.c + if ((markceiling || markfloor) && + (floorclip[rw_x] <= ceilingclip[rw_x] + 1)) { + solidcol[rw_x] = 1; didsolidcol = 1; + } + + // save texturecol for backdrawing of masked mid texture + if (maskedtexture) + maskedtexturecol[rw_x] = texturecolumn; + } + + rw_scale += rw_scalestep; + topfrac += topstep; + bottomfrac += bottomstep; + } +} + +// killough 5/2/98: move from r_main.c, made static, simplified + +static fixed_t R_PointToDist(fixed_t x, fixed_t y) +{ + fixed_t dx = D_abs(x - viewx); + fixed_t dy = D_abs(y - viewy); + + if (dy > dx) + { + fixed_t t = dx; + dx = dy; + dy = t; + } + + return FixedDiv(dx, finesine[(tantoangle[FixedDiv(dy,dx) >> DBITS] + + ANG90) >> ANGLETOFINESHIFT]); +} + +// +// R_StoreWallRange +// A wall segment will be drawn +// between start and stop pixels (inclusive). +// +void R_StoreWallRange(const int start, const int stop) +{ + const int shift_bits = 1; + int_64_t dx, dy, dx1, dy1, len, dist; + + if (ds_p == drawsegs+maxdrawsegs) // killough 1/98 -- fix 2s line HOM + { + unsigned pos = ds_p - drawsegs; // jff 8/9/98 fix from ZDOOM1.14a + unsigned newmax = maxdrawsegs ? maxdrawsegs*2 : 128; // killough + drawsegs = realloc(drawsegs,newmax*sizeof(*drawsegs)); + ds_p = drawsegs + pos; // jff 8/9/98 fix from ZDOOM1.14a + maxdrawsegs = newmax; + } + + if(curline->miniseg == false) // figgi -- skip minisegs + curline->linedef->flags |= ML_MAPPED; + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + // proff 11/99: the rest of the calculations is not needed for OpenGL + ds_p++->curline = curline; + gld_AddWall(curline); + + return; + } +#endif + + +#ifdef 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->pangle + ANG90; // [crispy] use re-calculated angle + + // [Linguica] Fix long wall error + // shift right to avoid possibility of int64 overflow in rw_distance calculation + dx = ((int_64_t)curline->v2->px - curline->v1->px) >> shift_bits; + dy = ((int_64_t)curline->v2->py - curline->v1->py) >> shift_bits; + dx1 = ((int_64_t)viewx - curline->v1->px) >> shift_bits; + dy1 = ((int_64_t)viewy - curline->v1->py) >> shift_bits; + len = curline->length >> shift_bits; + + dist = (((dy * dx1 - dx * dy1) / len) << shift_bits); + rw_distance = (fixed_t)BETWEEN(INT_MIN, INT_MAX, dist); + + ds_p->x1 = rw_x = start; + ds_p->x2 = stop; + ds_p->curline = curline; + rw_stopx = stop+1; + + { // killough 1/6/98, 2/1/98: remove limit on openings + extern int *openings; // dropoff overflow + extern size_t maxopenings; + size_t pos = lastopening - openings; + size_t need = (rw_stopx - start)*sizeof(*lastopening) + pos; + if (need > maxopenings) + { + drawseg_t *ds; //jff 8/9/98 needed for fix from ZDoom + int *oldopenings = openings; // dropoff overflow + int *oldlast = lastopening; // dropoff overflow + + do + maxopenings = maxopenings ? maxopenings*2 : 16384; + while (need > maxopenings); + openings = realloc(openings, maxopenings * sizeof(*openings)); + lastopening = openings + pos; + + // jff 8/9/98 borrowed fix for openings from ZDOOM1.14 + // [RH] We also need to adjust the openings pointers that + // were already stored in drawsegs. + for (ds = drawsegs; ds < ds_p; ds++) + { +#define ADJUST(p) if (ds->p + ds->x1 >= oldopenings && ds->p + ds->x1 <= oldlast)\ + ds->p = ds->p - oldopenings + openings; + ADJUST (maskedtexturecol); + ADJUST (sprtopclip); + ADJUST (sprbottomclip); + } +#undef ADJUST + } + } // killough: end of code to remove limits on openings + + worldtop = frontsector->ceilingheight - viewz; + worldbottom = frontsector->floorheight - viewz; + + R_FixWiggle(frontsector); + + // calculate scale at both ends and step + + ds_p->scale1 = rw_scale = + R_ScaleFromGlobalAngle (viewangle + xtoviewangle[start]); + + if (stop > start) + { + ds_p->scale2 = R_ScaleFromGlobalAngle (viewangle + xtoviewangle[stop]); + ds_p->scalestep = rw_scalestep = (ds_p->scale2-rw_scale) / (stop-start); + } + else + ds_p->scale2 = ds_p->scale1; + + // calculate texture boundaries + // and decide if floor / ceiling marks are needed + + midtexture = toptexture = bottomtexture = maskedtexture = 0; + ds_p->maskedtexturecol = NULL; + + if (!backsector) + { + // single sided line + midtexture = texturetranslation[sidedef->midtexture]; + midtexheight = (linedef->r_flags & RF_MID_TILE) ? 0 : textureheight[midtexture] >> FRACBITS; + + // a single sided line is terminal, so it must mark ends + markfloor = markceiling = true; + + if (linedef->flags & ML_DONTPEGBOTTOM) + { // bottom of texture at bottom + fixed_t vtop = frontsector->floorheight + + textureheight[sidedef->midtexture]; + rw_midtexturemid = vtop - viewz; + } + else // top of texture at top + rw_midtexturemid = worldtop; + + rw_midtexturemid += FixedMod(sidedef->rowoffset, textureheight[midtexture]); + + 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 (linedef->r_flags & RF_CLOSED) { /* cph - closed 2S line e.g. door */ + // cph - killough's (outdated) comment follows - this deals with both + // "automap fixes", his and mine + // killough 1/17/98: this test is required if the fix + // for the automap bug (r_bsp.c) is used, or else some + // sprites will be displayed behind closed doors. That + // fix prevents lines behind closed doors with dropoffs + // from being displayed on the automap. + + ds_p->silhouette = SIL_BOTH; + ds_p->sprbottomclip = negonearray; + ds_p->bsilheight = INT_MAX; + ds_p->sprtopclip = screenheightarray; + ds_p->tsilheight = INT_MIN; + + } else { /* not solid - old code */ + + 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; + } + + 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; + } + } + + 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; + + markfloor = worldlow != worldbottom + || backsector->floorpic != frontsector->floorpic + || backsector->lightlevel != frontsector->lightlevel + + // killough 3/7/98: Add checks for (x,y) offsets + || backsector->floor_xoffs != frontsector->floor_xoffs + || backsector->floor_yoffs != frontsector->floor_yoffs + + // killough 4/15/98: prevent 2s normals + // from bleeding through deep water + || frontsector->heightsec != -1 + + // killough 4/17/98: draw floors if different light levels + || backsector->floorlightsec != frontsector->floorlightsec + ; + + markceiling = worldhigh != worldtop + || backsector->ceilingpic != frontsector->ceilingpic + || backsector->lightlevel != frontsector->lightlevel + + // killough 3/7/98: Add checks for (x,y) offsets + || backsector->ceiling_xoffs != frontsector->ceiling_xoffs + || backsector->ceiling_yoffs != frontsector->ceiling_yoffs + + // killough 4/15/98: prevent 2s normals + // from bleeding through fake ceilings + || (frontsector->heightsec != -1 && + frontsector->ceilingpic!=skyflatnum) + + // killough 4/17/98: draw ceilings if different light levels + || backsector->ceilinglightsec != frontsector->ceilinglightsec + ; + + if (backsector->ceilingheight <= frontsector->floorheight + || backsector->floorheight >= frontsector->ceilingheight) + markceiling = markfloor = true; // closed door + + if (worldhigh < worldtop) // top texture + { + toptexture = texturetranslation[sidedef->toptexture]; + toptexheight = (linedef->r_flags & RF_TOP_TILE) ? 0 : textureheight[toptexture] >> FRACBITS; + rw_toptexturemid = linedef->flags & ML_DONTPEGTOP ? worldtop : + backsector->ceilingheight+textureheight[sidedef->toptexture]-viewz; + rw_toptexturemid += FixedMod(sidedef->rowoffset, textureheight[toptexture]); + } + + if (worldlow > worldbottom) // bottom texture + { + bottomtexture = texturetranslation[sidedef->bottomtexture]; + bottomtexheight = (linedef->r_flags & RF_BOT_TILE) ? 0 : textureheight[bottomtexture] >> FRACBITS; + rw_bottomtexturemid = linedef->flags & ML_DONTPEGBOTTOM ? worldtop : + worldlow; + rw_bottomtexturemid += FixedMod(sidedef->rowoffset, textureheight[bottomtexture]); + } + + // 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) + { + rw_offset = (fixed_t)(((dx * dx1 + dy * dy1) / len) << shift_bits); + + rw_offset += sidedef->textureoffset + curline->offset; + + rw_centerangle = ANG90 + viewangle - rw_normalangle; + + rw_lightlevel = frontsector->lightlevel; + + // calculate light table + // use different light tables + // for horizontal / vertical / diagonal + // OPTIMIZE: get rid of LIGHTSEGSHIFT globally + if (!fixedcolormap) + { + walllights = GetLightTable(rw_lightlevel); + walllightsnext = GetLightTable(rw_lightlevel + 1); + } + } + + // Remember the vars used to determine fractional U texture + // coords for later - POPE + ds_p->rw_offset = rw_offset; + ds_p->rw_distance = rw_distance; + ds_p->rw_centerangle = rw_centerangle; + + // 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. + + // killough 3/7/98: add deep water check + if (frontsector->heightsec == -1) + { + 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 >>= invhgtbits; + worldbottom >>= invhgtbits; + + topstep = -FixedMul (rw_scalestep, worldtop); + topfrac = ((int_64_t)centeryfrac>>invhgtbits) - (((int_64_t)worldtop*rw_scale)>>FRACBITS); // R_WiggleFix + + bottomstep = -FixedMul (rw_scalestep,worldbottom); + bottomfrac = ((int_64_t)centeryfrac>>invhgtbits) - (((int_64_t)worldbottom*rw_scale)>>FRACBITS); // R_WiggleFix + + if (backsector) + { + worldhigh >>= invhgtbits; + worldlow >>= invhgtbits; + + if (worldhigh < worldtop) + { + pixhigh = ((int_64_t)centeryfrac>>invhgtbits) - (((int_64_t)worldhigh*rw_scale)>>FRACBITS); // R_WiggleFix + pixhighstep = -FixedMul (rw_scalestep,worldhigh); + } + if (worldlow > worldbottom) + { + pixlow = ((int_64_t)centeryfrac>>invhgtbits) - (((int_64_t)worldlow*rw_scale)>>FRACBITS); // R_WiggleFix + pixlowstep = -FixedMul (rw_scalestep,worldlow); + } + } + + // render it + if (markceiling) { + if (ceilingplane) // killough 4/11/98: add NULL ptr checks + ceilingplane = R_CheckPlane (ceilingplane, rw_x, rw_stopx-1); + else + markceiling = 0; + } + + if (markfloor) { + if (floorplane) // killough 4/11/98: add NULL ptr checks + /* cph 2003/04/18 - ceilingplane and floorplane might be the same + * visplane (e.g. if both skies); R_CheckPlane doesn't know about + * modifications to the plane that might happen in parallel with the check + * being made, so we have to override it and split them anyway if that is + * a possibility, otherwise the floor marking would overwrite the ceiling + * marking, resulting in HOM. */ + if (markceiling && ceilingplane == floorplane) + floorplane = R_DupPlane (floorplane, rw_x, rw_stopx-1); + else + floorplane = R_CheckPlane (floorplane, rw_x, rw_stopx-1); + else + markfloor = 0; + } + + didsolidcol = 0; + R_RenderSegLoop(); + + /* cph - if a column was made solid by this wall, we _must_ save full clipping info */ + if (backsector && didsolidcol) { + if (!(ds_p->silhouette & SIL_BOTTOM)) { + ds_p->silhouette |= SIL_BOTTOM; + ds_p->bsilheight = backsector->floorheight; + } + if (!(ds_p->silhouette & SIL_TOP)) { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = backsector->ceilingheight; + } + } + + // save sprite clipping info + if ((ds_p->silhouette & SIL_TOP || maskedtexture) && !ds_p->sprtopclip) + { + memcpy (lastopening, ceilingclip+start, sizeof(*lastopening)*(rw_stopx-start)); // dropoff overflow + 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)); // dropoff overflow + 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/src/r_segs.h b/src/r_segs.h new file mode 100644 index 0000000..c5b29a6 --- /dev/null +++ b/src/r_segs.h @@ -0,0 +1,44 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh module, drawing LineSegs from BSP. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_SEGS__ +#define __R_SEGS__ + +#ifdef __GNUG__ +#pragma interface +#endif + +void R_RenderMaskedSegRange(drawseg_t *ds, int x1, int x2); +void R_StoreWallRange(const int start, const int stop); + +#endif diff --git a/src/r_sky.c b/src/r_sky.c new file mode 100644 index 0000000..cb63044 --- /dev/null +++ b/src/r_sky.c @@ -0,0 +1,123 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * 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? + * + *-----------------------------------------------------------------------------*/ + +#ifdef __GNUG__ +#pragma implementation "r_sky.h" +#endif +#include "r_sky.h" +#include "r_main.h" +#include "e6y.h" + +// +// sky mapping +// +int skyflatnum; +int skytexture; +int skytexturemid; + +int r_stretchsky = 1; +int skystretch; +fixed_t freelookviewheight; + +// +// R_InitSkyMap +// Called whenever the view size changes. +// +void R_InitSkyMap(void) +{ + if (!GetMouseLook()) + { + skystretch = false; + skytexturemid = 100*FRACUNIT; + if (viewwidth != 0) + { + skyiscale = (fixed_t)(((uint_64_t)FRACUNIT * SCREENWIDTH * 200) / (viewwidth * SCREENHEIGHT)); + } + } + else + { + int skyheight; + + if (!textureheight) + return; + + // There are various combinations for sky rendering depending on how tall the sky is: + // h < 128: Unstretched and tiled, centered on horizon + // 128 <= h < 200: Can possibly be stretched. When unstretched, the baseline is + // 28 rows below the horizon so that the top of the texture + // aligns with the top of the screen when looking straight ahead. + // When stretched, it is scaled to 228 pixels with the baseline + // in the same location as an unstretched 128-tall sky, so the top + // of the texture aligns with the top of the screen when looking + // fully up. + // h == 200: Unstretched, baseline is on horizon, and top is at the top of + // the screen when looking fully up. + // h > 200: Unstretched, but the baseline is shifted down so that the top + // of the texture is at the top of the screen when looking fully up. + + skyheight = textureheight[skytexture]>>FRACBITS; + skystretch = false; + skytexturemid = 0; + if (skyheight >= 128 && skyheight < 200) + { + skystretch = (r_stretchsky && skyheight >= 128); + skytexturemid = -28*FRACUNIT; + } + else if (skyheight > 200) + { + skytexturemid = (200 - skyheight) << FRACBITS; + } + + if (viewwidth != 0 && viewheight != 0) + { + //skyiscale = 200 * FRACUNIT / freelookviewheight; + skyiscale = (fixed_t)(((uint_64_t)FRACUNIT * SCREENWIDTH * 200) / (viewwidth * SCREENHEIGHT)); + // line below is from zdoom, but it works incorrectly with prboom + // with widescreen resolutions (eg 1280x720) by some reasons + //skyiscale = (fixed_t)((int_64_t)skyiscale * FieldOfView / 2048); + } + + if (skystretch) + { + skyiscale = (fixed_t)((int_64_t)skyiscale * skyheight / SKYSTRETCH_HEIGHT); + skytexturemid = (int)((int_64_t)skytexturemid * skyheight / SKYSTRETCH_HEIGHT); + } + else + { + skytexturemid = 100*FRACUNIT; + } + } +} diff --git a/src/r_sky.h b/src/r_sky.h new file mode 100644 index 0000000..8adf976 --- /dev/null +++ b/src/r_sky.h @@ -0,0 +1,61 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Sky rendering. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_SKY__ +#define __R_SKY__ + +#include "m_fixed.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +/* SKY, store the number for name. */ +#define SKYFLATNAME "F_SKY1" + +/* The sky map is 256*128*4 maps. */ +#define ANGLETOSKYSHIFT 22 + +extern int skyflatnum; +extern int skytexture; +extern int skytexturemid; + +#define SKYSTRETCH_HEIGHT 228 +extern int r_stretchsky; +extern int skystretch; +extern fixed_t freelookviewheight; + +/* Called whenever the view size changes. */ +void R_InitSkyMap(void); + +#endif diff --git a/src/r_state.h b/src/r_state.h new file mode 100644 index 0000000..1691fc1 --- /dev/null +++ b/src/r_state.h @@ -0,0 +1,125 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh/render internal state variables (global). + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __R_STATE__ +#define __R_STATE__ + +// Need data structure definitions. +#include "d_player.h" +#include "r_data.h" + +#ifdef __GNUG__ +#pragma interface +#endif + + +// +// Refresh internal data structures, +// for rendering. +// + +// needed for texture pegging +extern fixed_t *textureheight; + +extern int scaledviewwidth; + +extern int firstflat, numflats; + +// for global animation +extern int *flattranslation; +extern int *texturetranslation; + +// Sprite.... +extern int firstspritelump; +extern int lastspritelump; +extern int numspritelumps; + +// +// Lookup tables for map data. +// +extern int numsprites; +extern spritedef_t *sprites; + +extern int numvertexes; +extern vertex_t *vertexes; + +extern int numsegs; +extern seg_t *segs; + +extern int numsectors; +extern sector_t *sectors; + +extern int numsubsectors; +extern subsector_t *subsectors; + +extern int numnodes; +extern node_t *nodes; + +extern int numlines; +extern line_t *lines; + +extern int numsides; +extern side_t *sides; + +extern int *sslines_indexes; +extern ssline_t *sslines; + +extern byte *map_subsectors; + +// +// 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]; + +// e6y: resolution limitation is removed +extern angle_t *xtoviewangle; // killough 2/8/98 + +extern int FieldOfView; + +extern fixed_t rw_distance; +extern angle_t rw_normalangle; + +// angle to line origin +extern int rw_angle1; + +extern visplane_t *floorplane; +extern visplane_t *ceilingplane; + +#endif diff --git a/src/r_things.c b/src/r_things.c new file mode 100644 index 0000000..0d69987 --- /dev/null +++ b/src/r_things.c @@ -0,0 +1,1478 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh of things, i.e. objects represented by sprites. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_bsp.h" +#include "r_segs.h" +#include "r_draw.h" +#include "r_things.h" +#include "r_fps.h" +#include "v_video.h" +#include "p_pspr.h" +#include "lprintf.h" +#include "e6y.h"//e6y + +#define BASEYCENTER 100 + +static int *clipbot = NULL; // killough 2/8/98: // dropoff overflow +static int *cliptop = NULL; // change to MAX_* // dropoff overflow + +// +// 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 pspriteiscale; +// proff 11/06/98: Added for high-res +fixed_t pspritexscale; +fixed_t pspriteyscale; +fixed_t pspriteiyscale; + +static const lighttable_t **spritelights; // killough 1/25/98 made static + +//e6y: added for GL +float pspriteyscale_f; +float pspritexscale_f; + +int sprites_doom_order; + +int health_bar; +int health_bar_full_length; +int health_bar_red; +int health_bar_yellow; +int health_bar_green; + +typedef struct drawseg_xrange_item_s +{ + short x1, x2; + drawseg_t *user; +} drawseg_xrange_item_t; + +typedef struct drawsegs_xrange_s +{ + drawseg_xrange_item_t *items; + int count; +} drawsegs_xrange_t; + +#define DS_RANGES_COUNT 3 +static drawsegs_xrange_t drawsegs_xranges[DS_RANGES_COUNT]; + +static drawseg_xrange_item_t *drawsegs_xrange; +static unsigned int drawsegs_xrange_size = 0; +static int drawsegs_xrange_count = 0; + +// constant arrays +// used for psprite clipping and initializing clipping + +// e6y: resolution limitation is removed +int *negonearray; // killough 2/8/98: // dropoff overflow +int *screenheightarray; // change to MAX_* // dropoff overflow + +// +// INITIALIZATION FUNCTIONS +// + +// variables used to look up and range check thing_t sprites patches + +spritedef_t *sprites; +int numsprites; + +#define MAX_SPRITE_FRAMES 29 /* Macroized -- killough 1/25/98 */ + +static spriteframe_t sprtemp[MAX_SPRITE_FRAMES]; +static int maxframe; + +void R_InitSpritesRes(void) +{ + if (xtoviewangle) free(xtoviewangle); + if (negonearray) free(negonearray); + if (screenheightarray) free(screenheightarray); + + xtoviewangle = calloc(1, (SCREENWIDTH + 1) * sizeof(*xtoviewangle)); + negonearray = calloc(1, SCREENWIDTH * sizeof(*negonearray)); + screenheightarray = calloc(1, SCREENWIDTH * sizeof(*screenheightarray)); + + if (clipbot) free(clipbot); + + clipbot = calloc(1, 2 * SCREENWIDTH * sizeof(*clipbot)); + cliptop = clipbot + SCREENWIDTH; +} + +// +// R_InstallSpriteLump +// Local function for R_InitSprites. +// + +static void R_InstallSpriteLump(int lump, unsigned frame, + char rot, dboolean flipped) +{ + unsigned int rotation; + + if (rot >= '0' && rot <= '9') + { + rotation = rot - '0'; + } + else if (rot >= 'A') + { + rotation = rot - 'A' + 10; + } + else + { + rotation = 17; + } + + if (frame >= MAX_SPRITE_FRAMES || rotation > 16) + 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 + int r; + for (r = 14; r >= 0; r -= 2) + if (sprtemp[frame].lump[r] == -1) + { + sprtemp[frame].lump[r] = lump - firstspritelump; + if (flipped) + { + sprtemp[frame].flip |= (1 << r); + } + sprtemp[frame].rotate = false; //jff 4/24/98 if any subbed, rotless + } + return; + } + + // the lump is only used for one rotation + + if (rotation <= 8) + { + rotation = (rotation - 1) * 2; + } + else + { + rotation = (rotation - 9) * 2 + 1; + } + + if (sprtemp[frame].lump[rotation] == -1) + { + sprtemp[frame].lump[rotation] = lump - firstspritelump; + if (flipped) + { + sprtemp[frame].flip |= (1 << rotation); + } + sprtemp[frame].rotate = true; //jff 4/24/98 only change if rot used + } +} + +// +// R_InitSpriteDefs +// Pass a null terminated list of sprite names +// (4 chars exactly) to be used. +// +// Builds the sprite rotation matrixes to account +// for horizontally flipped sprites. +// +// Will report an error if the lumps are 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. +// +// 1/25/98, 1/31/98 killough : Rewritten for performance +// +// Empirically verified to have excellent hash +// properties across standard Doom sprites: + +#define R_SpriteNameHash(s) ((unsigned)((s)[0]-((s)[1]*3-(s)[3]*2-(s)[2])*2)) + +static void R_InitSpriteDefs(const char * const * namelist) +{ + size_t numentries = lastspritelump-firstspritelump+1; + struct { int index, next; } *hash; + int i; + + if (!numentries || !*namelist) + return; + + // count the number of sprite names + for (i=0; namelist[i]; i++) + ; + + numsprites = i; + + sprites = Z_Calloc(numsprites, sizeof(*sprites), PU_STATIC, NULL); + + // Create hash table based on just the first four letters of each sprite + // killough 1/31/98 + + hash = malloc(sizeof(*hash)*numentries); // allocate hash table + + for (i=0; (size_t)i= 0) + { + memset(sprtemp, -1, sizeof(sprtemp)); + for (k = 0; k < MAX_SPRITE_FRAMES; k++) + { + sprtemp[k].flip = 0; + } + + maxframe = -1; + do + { + register lumpinfo_t *lump = lumpinfo + j + firstspritelump; + + // Fast portable comparison -- killough + // (using int pointer cast is nonportable): + + if (!((lump->name[0] ^ spritename[0]) | + (lump->name[1] ^ spritename[1]) | + (lump->name[2] ^ spritename[2]) | + (lump->name[3] ^ spritename[3]))) + { + R_InstallSpriteLump(j+firstspritelump, + lump->name[4] - 'A', + lump->name[5], + false); + if (lump->name[6]) + R_InstallSpriteLump(j+firstspritelump, + lump->name[6] - 'A', + lump->name[7], + true); + } + } + while ((j = hash[j].next) >= 0); + + // check the frames that were found for completeness + if ((sprites[i].numframes = ++maxframe)) // killough 1/31/98 + { + int frame; + for (frame = 0; frame < maxframe; frame++) + { + switch (sprtemp[frame].rotate) + { + case -1: + // no rotations were found for that frame at all + //I_Error ("R_InitSprites: No patches found " + // "for %.8s frame %c", namelist[i], frame+'A'); + break; + + case 0: + // only the first rotation is needed + for (rot = 1; rot < 16; rot++) + { + sprtemp[frame].lump[rot] = sprtemp[frame].lump[0]; + } + // If the frame is flipped, they all should be + if (sprtemp[frame].flip & 1) + { + sprtemp[frame].flip = 0xFFFF; + } + break; + + case 1: + // must have all 8 frames + for (rot = 0; rot < 8; rot++) + { + if (sprtemp[frame].lump[rot * 2 + 1] == -1) + { + sprtemp[frame].lump[rot * 2 + 1] = sprtemp[frame].lump[rot * 2]; + if (sprtemp[frame].flip & (1 << (rot * 2))) + { + sprtemp[frame].flip |= 1 << (rot * 2 + 1); + } + } + if (sprtemp[frame].lump[rot * 2] == -1) + { + sprtemp[frame].lump[rot * 2] = sprtemp[frame].lump[rot * 2 + 1]; + if (sprtemp[frame].flip & (1 << (rot * 2 + 1))) + { + sprtemp[frame].flip |= 1 << (rot * 2); + } + } + + } + for (rot = 0; rot < 16; rot++) + { + if (sprtemp[frame].lump[rot] == -1) + I_Error ("R_InitSprites: Sprite %.8s frame %c " + "is missing rotations", + namelist[i], frame+'A'); + } + break; + } + } + + for (frame = 0; frame < maxframe; frame++) + { + if (sprtemp[frame].rotate == -1) + { + memset(&sprtemp[frame].lump, 0, sizeof(sprtemp[0].lump)); + sprtemp[frame].flip = 0; + sprtemp[frame].rotate = 0; + } + } + + // allocate space for the frames present and copy sprtemp to it + sprites[i].spriteframes = + Z_Malloc (maxframe * sizeof(spriteframe_t), PU_STATIC, NULL); + memcpy (sprites[i].spriteframes, sprtemp, + maxframe*sizeof(spriteframe_t)); + } + } + } + free(hash); // free hash table +} + +// +// GAME FUNCTIONS +// + +static vissprite_t *vissprites, **vissprite_ptrs; // killough +static int num_vissprite, num_vissprite_alloc, num_vissprite_ptrs; + +// +// R_InitSprites +// Called at program start. +// + +void R_InitSprites(const char * const *namelist) +{ + int i; + for (i=0; i= num_vissprite_alloc) // killough + { + size_t num_vissprite_alloc_prev = num_vissprite_alloc; + + num_vissprite_alloc = num_vissprite_alloc ? num_vissprite_alloc*2 : 128; + vissprites = realloc(vissprites,num_vissprite_alloc*sizeof(*vissprites)); + + //e6y: set all fields to zero + memset(vissprites + num_vissprite_alloc_prev, 0, + (num_vissprite_alloc - num_vissprite_alloc_prev)*sizeof(*vissprites)); + } + return vissprites + num_vissprite++; +} + +// +// R_DrawMaskedColumn +// Used for sprites and masked mid textures. +// Masked means: partly transparent, i.e. stored +// in posts/runs of opaque pixels. +// + +int *mfloorclip; // dropoff overflow +int *mceilingclip; // dropoff overflow +fixed_t spryscale; +int_64_t sprtopscreen; // R_WiggleFix + +void R_DrawMaskedColumn( + const rpatch_t *patch, + R_DrawColumn_f colfunc, + draw_column_vars_t *dcvars, + const rcolumn_t *column, + const rcolumn_t *prevcolumn, + const rcolumn_t *nextcolumn +) +{ + int i; + int_64_t topscreen; // R_WiggleFix + int_64_t bottomscreen; // R_WiggleFix + fixed_t basetexturemid = dcvars->texturemid; + + dcvars->texheight = patch->height; // killough 11/98 + for (i=0; inumPosts; i++) { + const rpost_t *post = &column->posts[i]; + + // calculate unclipped screen coordinates for post + topscreen = sprtopscreen + spryscale*post->topdelta; + bottomscreen = topscreen + spryscale*post->length; + + dcvars->yl = (int)((topscreen+FRACUNIT-1)>>FRACBITS); + dcvars->yh = (int)((bottomscreen-1)>>FRACBITS); + + if (dcvars->yh >= mfloorclip[dcvars->x]) + dcvars->yh = mfloorclip[dcvars->x]-1; + + if (dcvars->yl <= mceilingclip[dcvars->x]) + dcvars->yl = mceilingclip[dcvars->x]+1; + + // killough 3/2/98, 3/27/98: Failsafe against overflow/crash: + if (dcvars->yl >= 0 && dcvars->yl <= dcvars->yh && dcvars->yh < viewheight) + { + dcvars->source = column->pixels + post->topdelta; + dcvars->prevsource = prevcolumn->pixels + post->topdelta; + dcvars->nextsource = nextcolumn->pixels + post->topdelta; + + dcvars->texturemid = basetexturemid - (post->topdelta<edgeslope = post->slope; + // Drawn by either R_DrawColumn + // or (SHADOW) R_DrawFuzzColumn. + dcvars->drawingmasked = 1; // POPE + colfunc (dcvars); + dcvars->drawingmasked = 0; // POPE + } + } + dcvars->texturemid = basetexturemid; +} + +static void R_SetSpritelights(int lightlevel) +{ + int lightnum = (lightlevel >> LIGHTSEGSHIFT) + (extralight * LIGHTBRIGHT); + spritelights = scalelight[BETWEEN(0, LIGHTLEVELS - 1, lightnum)]; +} + + +// +// R_DrawVisSprite +// mfloorclip and mceilingclip should also be set. +// +// CPhipps - new wad lump handling, *'s to const*'s +static void R_DrawVisSprite(vissprite_t *vis) +{ + int texturecolumn; + fixed_t frac; + const rpatch_t *patch = R_CachePatchNum(vis->patch+firstspritelump); + R_DrawColumn_f colfunc; + draw_column_vars_t dcvars; + enum draw_filter_type_e filter; + enum draw_filter_type_e filterz; + + R_SetDefaultDrawColumnVars(&dcvars); + if (vis->mobjflags & MF_PLAYERSPRITE) { + dcvars.edgetype = drawvars.patch_edges; + filter = drawvars.filterpatch; + filterz = RDRAW_FILTER_POINT; + } else { + dcvars.edgetype = drawvars.sprite_edges; + filter = drawvars.filtersprite; + filterz = drawvars.filterz; + } + + dcvars.colormap = vis->colormap; + dcvars.nextcolormap = dcvars.colormap; // for filtering -- POPE + + // killough 4/11/98: rearrange and handle translucent sprites + // mixed with translucent/non-translucenct 2s normals + + if (!dcvars.colormap) // NULL colormap = shadow draw + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_FUZZ, filter, filterz); // killough 3/14/98 + else + // [FG] colored blood and gibs + if (vis->mobjflags & MF_COLOREDBLOOD) + { + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_TRANSLATED, filter, filterz); + dcvars.translation = colrngs[vis->color]; + } + else + if (vis->mobjflags & MF_TRANSLATION) + { + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_TRANSLATED, filter, filterz); + dcvars.translation = translationtables - 256 + + ((vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT-8) ); + } + else + if (vis->mobjflags & MF_TRANSLUCENT && general_translucency) // phares + { + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_TRANSLUCENT, filter, filterz); + tranmap = main_tranmap; // killough 4/11/98 + } + else + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_STANDARD, filter, filterz); // killough 3/14/98, 4/11/98 + +// proff 11/06/98: Changed for high-res + dcvars.iscale = FixedDiv (FRACUNIT, vis->scale); + dcvars.texturemid = vis->texturemid; + frac = vis->startfrac; + if (filter == RDRAW_FILTER_LINEAR) + frac -= (FRACUNIT>>1); + spryscale = vis->scale; + sprtopscreen = centeryfrac - FixedMul(dcvars.texturemid,spryscale); + + // check to see if weapon is a vissprite + if(vis->mobjflags & MF_PLAYERSPRITE) + { + // [FG] fix garbage lines at the top of weapon sprites + dcvars.iscale = pspriteiyscale; + dcvars.texturemid += FixedMul(((centery - viewheight/2)<x1 ; dcvars.x<=vis->x2 ; dcvars.x++, frac += vis->xiscale) + { + texturecolumn = frac>>FRACBITS; + dcvars.texu = frac; + + R_DrawMaskedColumn( + patch, + colfunc, + &dcvars, + R_GetPatchColumnClamped(patch, texturecolumn), + R_GetPatchColumnClamped(patch, texturecolumn-1), + R_GetPatchColumnClamped(patch, texturecolumn+1) + ); + } + R_UnlockPatchNum(vis->patch+firstspritelump); // cph - release lump +} + +int r_near_clip_plane = MINZ; + +void R_SetClipPlanes(void) +{ + // thing is behind view plane? +#ifdef GL_DOOM + if ((V_GetMode() == VID_MODEGL) && + (HaveMouseLook() || (render_fov > FOV90)) && + (!render_paperitems || simple_shadows.loaded)) + { + r_near_clip_plane = -(FRACUNIT * MAX(64, simple_shadows.max_radius)); + } + else +#endif + { + r_near_clip_plane = MINZ; + } +} + +// +// R_ProjectSprite +// Generates a vissprite for a thing if it might be visible. +// + +static void R_ProjectSprite (mobj_t* thing, int lightlevel) +{ + fixed_t gzt, gzb; // killough 3/27/98 + fixed_t tx; + fixed_t xscale; + int x1; + int x2; + spritedef_t *sprdef; + spriteframe_t *sprframe; + int lump; + dboolean flip; + vissprite_t *vis; + fixed_t iscale; + int heightsec; // killough 3/27/98 + + // transform the origin point + //e6y + fixed_t tr_x, tr_y; + fixed_t fx, fy, fz; + fixed_t gxt, gyt; + fixed_t tz, tz2; + int width; + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + gld_ProjectSprite(thing, lightlevel); + return; + } +#endif + + if (!paused && movement_smooth) + { + fx = thing->PrevX + FixedMul (tic_vars.frac, thing->x - thing->PrevX); + fy = thing->PrevY + FixedMul (tic_vars.frac, thing->y - thing->PrevY); + fz = thing->PrevZ + FixedMul (tic_vars.frac, thing->z - thing->PrevZ); + } + else + { + fx = thing->x; + fy = thing->y; + fz = thing->z; + } + + tr_x = fx - viewx; + tr_y = fy - viewy; + + gxt = FixedMul(tr_x,viewcos); + gyt = -FixedMul(tr_y,viewsin); + + tz = gxt-gyt; + + // thing is behind view plane? + if (tz < r_near_clip_plane) + return; + + xscale = FixedDiv(projection, tz); + + gxt = -FixedMul(tr_x,viewsin); + gyt = FixedMul(tr_y,viewcos); + tx = -(gyt+gxt); + + // too far off the side? + if (D_abs(tx)>(tz<<2)) + return; + + // decide which patch to use for sprite relative to player +#ifdef RANGECHECK + if ((unsigned) thing->sprite >= (unsigned)numsprites) + I_Error ("R_ProjectSprite: Invalid sprite number %i", thing->sprite); +#endif + + sprdef = &sprites[thing->sprite]; + +#ifdef RANGECHECK + if ((thing->frame&FF_FRAMEMASK) >= sprdef->numframes) + I_Error ("R_ProjectSprite: Invalid sprite frame %i : %i", thing->sprite, + thing->frame); +#endif + + if (!sprdef->spriteframes) + I_Error ("R_ProjectSprite: Missing spriteframes %i : %i", thing->sprite, + thing->frame); + + sprframe = &sprdef->spriteframes[thing->frame & FF_FRAMEMASK]; + + if (sprframe->rotate) + { + // choose a different rotation based on player view + angle_t rot; + angle_t ang = R_PointToAngle2(viewx, viewy, fx, fy); + if (sprframe->lump[0] == sprframe->lump[1]) + { + rot = (ang - thing->angle + (angle_t)(ANG45/2)*9) >> 28; + } + else + { + rot = (ang - thing->angle + (angle_t)(ANG45 / 2) * 9 - + (angle_t)(ANG180 / 16)) >> 28; + } + lump = sprframe->lump[rot]; + flip = (dboolean)(sprframe->flip & (1 << rot)); + } + else + { + // use single rotation for all views + lump = sprframe->lump[0]; + flip = (dboolean)(sprframe->flip & 1); + } + + { + const rpatch_t* patch = R_CachePatchNum(lump+firstspritelump); + thing->patch_width = patch->width; + + /* calculate edges of the shape + * cph 2003/08/1 - fraggle points out that this offset must be flipped + * if the sprite is flipped; e.g. FreeDoom imp is messed up by this. */ + if (flip) { + tx -= (patch->width - patch->leftoffset) << FRACBITS; + } else { + tx -= patch->leftoffset << FRACBITS; + } + x1 = (centerxfrac + FixedMul(tx,xscale)) >> FRACBITS; + + tx += patch->width<> FRACBITS); + + gzt = fz + (patch->topoffset << FRACBITS); + gzb = gzt - (patch->height << FRACBITS); + width = patch->width; + R_UnlockPatchNum(lump+firstspritelump); + } + + // off the side? + if (x1 > viewwidth || x2 < 0) + return; + + // [RH] Reject sprites that are off the top or bottom of the screen + tz2 = FixedMul(tr_x, viewtancos) + FixedMul(tr_y, viewtansin); + if (FixedMul(globaluclip, tz2) > viewz - gzb || + FixedMul(globaldclip, tz2) < viewz - gzt) + { + return; + } + + // killough 3/27/98: exclude things totally separated + // from the viewer, by either water or fake ceilings + // killough 4/11/98: improve sprite clipping for underwater/fake ceilings + + heightsec = thing->subsector->sector->heightsec; + + if (heightsec != -1) // only clip things which are in special sectors + { + int phs = viewplayer->mo->subsector->sector->heightsec; + if (phs != -1 && viewz < sectors[phs].floorheight ? + fz >= sectors[heightsec].floorheight : + gzt < sectors[heightsec].floorheight) + return; + if (phs != -1 && viewz > sectors[phs].ceilingheight ? + gzt < sectors[heightsec].ceilingheight && + viewz >= sectors[heightsec].ceilingheight : + fz >= sectors[heightsec].ceilingheight) + return; + } + + //e6y FIXME!!! + if (thing == players[displayplayer].mo && walkcamera.type != 2) +// if (thing->player && thing->player == &players[displayplayer] && walkcamera.type != 2) + return; + + // store information in a vissprite + vis = R_NewVisSprite (); + + vis->gx = fx; + vis->gy = fy; + vis->gz = fz; + + //vis->isplayersprite = false; // e6y + + // killough 3/27/98: save sector for special clipping later + vis->heightsec = heightsec; + + vis->mobjflags = thing->flags; +// proff 11/06/98: Changed for high-res + vis->scale = FixedDiv(projectiony, tz); + vis->gzt = gzt; // killough 3/27/98 + vis->texturemid = vis->gzt - viewz; + vis->x1 = x1 < 0 ? 0 : x1; + vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; + iscale = FixedDiv (FRACUNIT, xscale); + vis->color = thing->bloodcolor; + + if (flip) + { + vis->startfrac = (width<xiscale = -iscale; + } + else + { + vis->startfrac = 0; + vis->xiscale = iscale; + } + + if (vis->x1 > x1) + vis->startfrac += vis->xiscale*(vis->x1-x1); + vis->patch = lump; + + R_SetSpritelights(lightlevel); + + // get light level + if (thing->flags & MF_SHADOW) + vis->colormap = NULL; // shadow draw + else if (fixedcolormap) + vis->colormap = fixedcolormap; // fixed map + else if (thing->frame & FF_FULLBRIGHT) + vis->colormap = fullcolormap; // full bright // killough 3/20/98 + else + { // diminished light + int index = (int)(((int_64_t)xscale * 160 / wide_centerx) >> LIGHTSCALESHIFT); + if (index >= MAXLIGHTSCALE) + index = MAXLIGHTSCALE - 1; + vis->colormap = spritelights[index]; + } +} + +// +// R_AddSprites +// During BSP traversal, this adds sprites by sector. +// +// killough 9/18/98: add lightlevel as parameter, fixing underwater lighting +void R_AddSprites(subsector_t* subsec, int lightlevel) +{ + sector_t* sec=subsec->sector; + mobj_t *thing; + + if (compatibility_level <= boom_202_compatibility) + lightlevel = sec->lightlevel; + + // Handle all things in sector. + +#ifdef GL_DOOM + if (show_alive) + { + if (show_alive == 1) + { + for (thing = sec->thinglist; thing; thing = thing->snext) + { + if (!ALIVE(thing)) + R_ProjectSprite(thing, lightlevel); + } + } + } + else +#endif + { + for (thing = sec->thinglist; thing; thing = thing->snext) + { + R_ProjectSprite(thing, lightlevel); + } + } +} + +// +// R_AddAllAliveMonstersSprites +// Add all alive monsters. +// +void R_AddAllAliveMonstersSprites(void) +{ + int i; + sector_t* sec; + mobj_t *thing; + + for (i = 0; i < numsectors; i++) + { + sec = §ors[i]; + for (thing = sec->thinglist; thing; thing = thing->snext) + { + if (ALIVE(thing)) + { + thing->flags |= MF_NO_DEPTH_TEST; + R_ProjectSprite(thing, 255); + thing->flags &= ~MF_NO_DEPTH_TEST; + } + } + } +} + +// [crispy] apply bobbing (or centering) to the player's weapon sprite +static void R_ApplyWeaponBob (fixed_t *sx, dboolean bobx, fixed_t *sy, dboolean boby) +{ + const angle_t angle = (128 * leveltime) & FINEMASK; + + if (sx) + { + *sx = FRACUNIT; + + if (bobx) + { + *sx += FixedMul(viewplayer->bob, finecosine[angle]); + } + } + + if (sy) + { + *sy = 32 * FRACUNIT; // [crispy] WEAPONTOP + + if (boby) + { + *sy += FixedMul(viewplayer->bob, finesine[angle & (FINEANGLES / 2 - 1)]); + } + } +} + +// +// R_DrawPSprite +// + +static void R_DrawPSprite (pspdef_t *psp) +{ + int x1, x2; + spritedef_t *sprdef; + spriteframe_t *sprframe; + int lump; + dboolean flip; + vissprite_t *vis; + vissprite_t avis; + int width; + fixed_t topoffset; + fixed_t psp_sx = psp->sx; + fixed_t psp_sy = psp->sy; + + // decide which patch to use + +#ifdef RANGECHECK + if ( (unsigned)psp->state->sprite >= (unsigned)numsprites) + I_Error ("R_ProjectSprite: Invalid sprite number %i", psp->state->sprite); +#endif + + sprdef = &sprites[psp->state->sprite]; + +#ifdef RANGECHECK + if ( (psp->state->frame & FF_FRAMEMASK) >= sprdef->numframes) + I_Error ("R_ProjectSprite: Invalid sprite frame %i : %li", + psp->state->sprite, psp->state->frame); +#endif + + sprframe = &sprdef->spriteframes[psp->state->frame & FF_FRAMEMASK]; + + lump = sprframe->lump[0]; + flip = (dboolean)(sprframe->flip & 1); + + // [crispy] center the weapon sprite horizontally and vertically + if (weapon_attack_alignment && viewplayer->attackdown && !psp->state->misc1) + { + const weaponinfo_t *const winfo = &weaponinfo[viewplayer->readyweapon]; + const int state = viewplayer->psprites[ps_weapon].state - states; + + R_ApplyWeaponBob(&psp_sx, weapon_attack_alignment == CENTERWEAPON_BOB, NULL, false); + + // [crispy] don't center vertically during lowering and raising states + if (weapon_attack_alignment >= CENTERWEAPON_HORVER && + state != winfo->downstate && state != winfo->upstate) + { + R_ApplyWeaponBob(NULL, false, &psp_sy, weapon_attack_alignment == CENTERWEAPON_BOB); + } + } + + { + const rpatch_t* patch = R_CachePatchNum(lump+firstspritelump); + // calculate edges of the shape + fixed_t tx; + tx = psp_sx-160*FRACUNIT; + + tx -= patch->leftoffset<>FRACBITS; + + tx += patch->width<>FRACBITS) - 1; + + width = patch->width; + topoffset = patch->topoffset< viewwidth) + return; + + // store information in a vissprite + vis = &avis; + vis->mobjflags = MF_PLAYERSPRITE; + // killough 12/98: fix psprite positioning problem + vis->texturemid = (BASEYCENTER<texturemid -= psprite_offset; + + vis->x1 = x1 < 0 ? 0 : x1; + vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; +// proff 11/06/98: Added for high-res + vis->scale = pspriteyscale; + + if (flip) + { + vis->xiscale = -pspriteiscale; + vis->startfrac = (width<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 = fullcolormap; // full bright // killough 3/20/98 + else + // e6y: original code is restored + vis->colormap = spritelights[MAXLIGHTSCALE-1]; // local light + + //e6y: interpolation for weapon bobbing + if (movement_smooth) + { + typedef struct interpolate_s + { + int x1; + int x1_prev; + int texturemid; + int texturemid_prev; + int lump; + } psp_interpolate_t; + + static psp_interpolate_t psp_inter; + + if (realframe) + { + psp_inter.x1 = psp_inter.x1_prev; + psp_inter.texturemid = psp_inter.texturemid_prev; + } + + psp_inter.x1_prev = vis->x1; + psp_inter.texturemid_prev = vis->texturemid; + + if (lump == psp_inter.lump) + { + int deltax = vis->x2 - vis->x1; + vis->x1 = psp_inter.x1 + FixedMul (tic_vars.frac, (vis->x1 - psp_inter.x1)); + vis->x2 = vis->x1 + deltax; + vis->texturemid = psp_inter.texturemid + FixedMul (tic_vars.frac, (vis->texturemid - psp_inter.texturemid)); + } + else + { + psp_inter.x1 = vis->x1; + psp_inter.texturemid = vis->texturemid; + psp_inter.lump=lump; + } + } + + // proff 11/99: don't use software stuff in OpenGL + if (V_GetMode() != VID_MODEGL) + { + R_DrawVisSprite(vis); + } +#ifdef GL_DOOM + else + { + int lightlevel; + sector_t tmpsec; + int floorlightlevel, ceilinglightlevel; + + if ((vis->colormap==fixedcolormap) || (vis->colormap==fullcolormap)) + lightlevel=255; + else + { +// lightlevel = (viewplayer->mo->subsector->sector->lightlevel) + (extralight << LIGHTSEGSHIFT); + R_FakeFlat( viewplayer->mo->subsector->sector, &tmpsec, + &floorlightlevel, &ceilinglightlevel, false); + lightlevel = ((floorlightlevel+ceilinglightlevel) >> 1) + (extralight << LIGHTSEGSHIFT); + if (!gl_hardware_gamma) + lightlevel += usegamma * 16; + + if (lightlevel < 0) + lightlevel = 0; + else if (lightlevel >= 255) + lightlevel = 255; + } + gld_DrawWeapon(lump,vis,lightlevel); + } +#endif +} + +// +// R_DrawPlayerSprites +// + +void R_DrawPlayerSprites(void) +{ + int i; + pspdef_t *psp; + + if (walkcamera.type != 0) + return; + + // get light level + R_SetSpritelights(viewplayer->mo->subsector->sector->lightlevel); + + // clip to screen bounds + mfloorclip = screenheightarray; + mceilingclip = negonearray; + + // add all active psprites + for (i=0, psp=viewplayer->psprites; istate) + R_DrawPSprite (psp); +} + +// +// R_SortVisSprites +// +// Rewritten by Lee Killough to avoid using unnecessary +// linked lists, and to use faster sorting algorithm. +// + +#ifdef DJGPP + +// killough 9/22/98: inlined memcpy of pointer arrays +// CPhipps - added memory as modified +#define bcopyp(d, s, n) asm(" cld; rep; movsl;" :: "D"(d), "S"(s), "c"(n) : "%cc", "%esi", "%edi", "%ecx", "memory") + +#else + +#define bcopyp(d, s, n) memcpy(d, s, (n) * sizeof(void *)) + +#endif + +// killough 9/2/98: merge sort + +static void msort(vissprite_t **s, vissprite_t **t, int n) +{ + if (n >= 16) + { + int n1 = n/2, n2 = n - n1; + vissprite_t **s1 = s, **s2 = s + n1, **d = t; + + msort(s1, t, n1); + msort(s2, t, n2); + + while ((*s1)->scale > (*s2)->scale ? + (*d++ = *s1++, --n1) : (*d++ = *s2++, --n2)); + + if (n2) + bcopyp(d, s2, n2); + else + bcopyp(d, s1, n1); + + bcopyp(s, t, n); + } + else + { + int i; + for (i = 1; i < n; i++) + { + vissprite_t *temp = s[i]; + if (s[i-1]->scale < temp->scale) + { + int j = i; + while ((s[j] = s[j-1])->scale < temp->scale && --j); + s[j] = temp; + } + } + } +} + +void R_SortVisSprites (void) +{ + if (num_vissprite) + { + int i = num_vissprite; + + // If we need to allocate more pointers for the vissprites, + // allocate as many as were allocated for sprites -- killough + // killough 9/22/98: allocate twice as many + + if (num_vissprite_ptrs < num_vissprite*2) + { + free(vissprite_ptrs); // better than realloc -- no preserving needed + vissprite_ptrs = malloc((num_vissprite_ptrs = num_vissprite_alloc*2) + * sizeof *vissprite_ptrs); + } + + if (sprites_doom_order) + { + while (--i>=0) + vissprite_ptrs[num_vissprite-i-1] = vissprites+i; + } + else + { + while (--i>=0) + vissprite_ptrs[i] = vissprites+i; + } + + // killough 9/22/98: replace qsort with merge sort, since the keys + // are roughly in order to begin with, due to BSP rendering. + + msort(vissprite_ptrs, vissprite_ptrs + num_vissprite, num_vissprite); + } +} + +// +// R_DrawSprite +// + +static void R_DrawSprite (vissprite_t* spr) +{ + drawseg_t *ds; + int x; + int r1; + int r2; + fixed_t scale; + fixed_t lowscale; + + for (x = spr->x1 ; x<=spr->x2 ; x++) + clipbot[x] = -2; + for (x = spr->x1 ; x<=spr->x2 ; 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. + + // Modified by Lee Killough: + // (pointer check was originally nonportable + // and buggy, by going past LEFT end of array): + + // e6y: optimization + if (drawsegs_xrange_size) + { + const drawseg_xrange_item_t *last = &drawsegs_xrange[drawsegs_xrange_count - 1]; + drawseg_xrange_item_t *curr = &drawsegs_xrange[-1]; + while (++curr <= last) + { + // determine if the drawseg obscures the sprite + if (curr->x1 > spr->x2 || curr->x2 < spr->x1) + continue; // does not cover sprite + + ds = curr->user; + + if (ds->scale1 > ds->scale2) + { + lowscale = ds->scale2; + scale = ds->scale1; + } + else + { + lowscale = ds->scale1; + scale = ds->scale2; + } + + if (scale < spr->scale || (lowscale < spr->scale && + !R_PointOnSegSide (spr->gx, spr->gy, ds->curline))) + { + if (ds->maskedtexturecol) // masked mid texture? + { + r1 = ds->x1 < spr->x1 ? spr->x1 : ds->x1; + r2 = ds->x2 > spr->x2 ? spr->x2 : ds->x2; + R_RenderMaskedSegRange(ds, r1, r2); + } + continue; // seg is behind sprite + } + + r1 = ds->x1 < spr->x1 ? spr->x1 : ds->x1; + r2 = ds->x2 > spr->x2 ? spr->x2 : ds->x2; + + // clip this piece of the sprite + // killough 3/27/98: optimized and made much shorter + + if (ds->silhouette&SIL_BOTTOM && spr->gz < ds->bsilheight) //bottom sil + for (x=r1 ; x<=r2 ; x++) + if (clipbot[x] == -2) + clipbot[x] = ds->sprbottomclip[x]; + + if (ds->silhouette&SIL_TOP && spr->gzt > ds->tsilheight) // top sil + for (x=r1 ; x<=r2 ; x++) + if (cliptop[x] == -2) + cliptop[x] = ds->sprtopclip[x]; + } + } + + // killough 3/27/98: + // Clip the sprite against deep water and/or fake ceilings. + // killough 4/9/98: optimize by adding mh + // killough 4/11/98: improve sprite clipping for underwater/fake ceilings + // killough 11/98: fix disappearing sprites + + if (spr->heightsec != -1) // only things in specially marked sectors + { + fixed_t h,mh; + int phs = viewplayer->mo->subsector->sector->heightsec; + if ((mh = sectors[spr->heightsec].floorheight) > spr->gz && + (h = centeryfrac - FixedMul(mh-=viewz, spr->scale)) >= 0 && + (h >>= FRACBITS) < viewheight) { + if (mh <= 0 || (phs != -1 && viewz > sectors[phs].floorheight)) + { // clip bottom + for (x=spr->x1 ; x<=spr->x2 ; x++) + if (clipbot[x] == -2 || h < clipbot[x]) + clipbot[x] = h; + } + else // clip top + if (phs != -1 && viewz <= sectors[phs].floorheight) // killough 11/98 + for (x=spr->x1 ; x<=spr->x2 ; x++) + if (cliptop[x] == -2 || h > cliptop[x]) + cliptop[x] = h; + } + + if ((mh = sectors[spr->heightsec].ceilingheight) < spr->gzt && + (h = centeryfrac - FixedMul(mh-viewz, spr->scale)) >= 0 && + (h >>= FRACBITS) < viewheight) { + if (phs != -1 && viewz >= sectors[phs].ceilingheight) + { // clip bottom + for (x=spr->x1 ; x<=spr->x2 ; x++) + if (clipbot[x] == -2 || h < clipbot[x]) + clipbot[x] = h; + } + else // clip top + for (x=spr->x1 ; x<=spr->x2 ; x++) + if (cliptop[x] == -2 || h > cliptop[x]) + cliptop[x] = h; + } + } + // killough 3/27/98: end special clipping for deep water / fake ceilings + + // 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; + + for (x = spr->x1 ; x<=spr->x2 ; x++) + if (cliptop[x] == -2) + cliptop[x] = -1; + + mfloorclip = clipbot; + mceilingclip = cliptop; + R_DrawVisSprite (spr); +} + +// +// R_DrawMasked +// + +void R_DrawMasked(void) +{ + int i; + drawseg_t *ds; + int cx = SCREENWIDTH / 2; + + R_SortVisSprites(); + + // e6y + // Reducing of cache misses in the following R_DrawSprite() + // Makes sense for scenes with huge amount of drawsegs. + // ~12% of speed improvement on epic.wad map05 + for(i = 0; i < DS_RANGES_COUNT; i++) + drawsegs_xranges[i].count = 0; + + if (num_vissprite > 0) + { + if (drawsegs_xrange_size < maxdrawsegs) + { + drawsegs_xrange_size = 2 * maxdrawsegs; + for(i = 0; i < DS_RANGES_COUNT; i++) + { + drawsegs_xranges[i].items = realloc( + drawsegs_xranges[i].items, + drawsegs_xrange_size * sizeof(drawsegs_xranges[i].items[0])); + } + } + for (ds = ds_p; ds-- > drawsegs;) + { + if (ds->silhouette || ds->maskedtexturecol) + { + drawsegs_xranges[0].items[drawsegs_xranges[0].count].x1 = ds->x1; + drawsegs_xranges[0].items[drawsegs_xranges[0].count].x2 = ds->x2; + drawsegs_xranges[0].items[drawsegs_xranges[0].count].user = ds; + + // e6y: ~13% of speed improvement on sunder.wad map10 + if (ds->x1 < cx) + { + drawsegs_xranges[1].items[drawsegs_xranges[1].count] = + drawsegs_xranges[0].items[drawsegs_xranges[0].count]; + drawsegs_xranges[1].count++; + } + if (ds->x2 >= cx) + { + drawsegs_xranges[2].items[drawsegs_xranges[2].count] = + drawsegs_xranges[0].items[drawsegs_xranges[0].count]; + drawsegs_xranges[2].count++; + } + + drawsegs_xranges[0].count++; + } + } + } + + // draw all vissprites back to front + + rendered_vissprites = num_vissprite; + for (i = num_vissprite ;--i>=0; ) + { + vissprite_t* spr = vissprite_ptrs[i]; + + if (spr->x2 < cx) + { + drawsegs_xrange = drawsegs_xranges[1].items; + drawsegs_xrange_count = drawsegs_xranges[1].count; + } + else if (spr->x1 >= cx) + { + drawsegs_xrange = drawsegs_xranges[2].items; + drawsegs_xrange_count = drawsegs_xranges[2].count; + } + else + { + drawsegs_xrange = drawsegs_xranges[0].items; + drawsegs_xrange_count = drawsegs_xranges[0].count; + } + + R_DrawSprite(vissprite_ptrs[i]); + } + + // render any remaining masked mid textures + + // Modified by Lee Killough: + // (pointer check was originally nonportable + // and buggy, by going past LEFT end of array): + + // for (ds=ds_p-1 ; ds >= drawsegs ; ds--) old buggy code + + for (ds=ds_p ; ds-- > drawsegs ; ) // new -- killough + if (ds->maskedtexturecol) + R_RenderMaskedSegRange(ds, ds->x1, ds->x2); + + // draw the psprites on top of everything + // but does not draw on side views + if (!viewangleoffset && !viewpitchoffset) + R_DrawPlayerSprites (); +} diff --git a/src/r_things.h b/src/r_things.h new file mode 100644 index 0000000..2267cb8 --- /dev/null +++ b/src/r_things.h @@ -0,0 +1,99 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Rendering of moving objects, sprites. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_THINGS__ +#define __R_THINGS__ + +#ifdef __GNUG__ +#pragma interface +#endif + +#include "r_draw.h" + +#define MINZ (FRACUNIT*4) + +extern int r_near_clip_plane; + +/* Constant arrays used for psprite clipping and initializing clipping. */ + +// e6y: resolution limitation is removed +extern int *negonearray; /* killough 2/8/98: */ // dropoff overflow +extern int *screenheightarray; /* change to MAX_* */ // dropoff overflow + +/* Vars for R_DrawMaskedColumn */ + +extern int *mfloorclip; // dropoff overflow +extern int *mceilingclip; // dropoff overflow +extern fixed_t spryscale; +extern int_64_t sprtopscreen; +extern fixed_t pspriteiscale; +/* proff 11/06/98: Added for high-res */ +extern fixed_t pspritexscale; +extern fixed_t pspriteyscale; +extern fixed_t pspriteiyscale; +//e6y: added for GL +extern float pspritexscale_f; +extern float pspriteyscale_f; + +typedef enum { + DOOM_ORDER_NONE, + DOOM_ORDER_STATIC, + DOOM_ORDER_DYNAMIC, + DOOM_ORDER_LAST +} sprite_doom_order_t; +extern int sprites_doom_order; + +extern int health_bar; +extern int health_bar_full_length; +extern int health_bar_red; +extern int health_bar_yellow; +extern int health_bar_green; + +void R_DrawMaskedColumn(const rpatch_t *patch, + R_DrawColumn_f colfunc, + draw_column_vars_t *dcvars, + const rcolumn_t *column, + const rcolumn_t *prevcolumn, + const rcolumn_t *nextcolumn); +void R_SortVisSprites(void); +void R_AddSprites(subsector_t* subsec, int lightlevel); +void R_AddAllAliveMonstersSprites(void); +void R_DrawPlayerSprites(void); +void R_InitSpritesRes(void); +void R_InitSprites(const char * const * namelist); +void R_ClearSprites(void); +void R_DrawMasked(void); + +void R_SetClipPlanes(void); + +#endif diff --git a/src/s_advsound.c b/src/s_advsound.c new file mode 100644 index 0000000..415c4d5 --- /dev/null +++ b/src/s_advsound.c @@ -0,0 +1,171 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: Support MUSINFO lump (dynamic music changing) + * + *-----------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomstat.h" +#include "doomtype.h" +#include "d_main.h" +#include "p_mobj.h" +#include "m_misc.h" +#include "sounds.h" +#include "s_sound.h" +#include "i_sound.h" +#include "r_defs.h" +#include "sc_man.h" +#include "w_wad.h" +#include "lprintf.h" + +#include "s_advsound.h" + +#define TIDNUM(x) ((int)(x->iden_nums & 0xFFFF)) // thing identifier + +musinfo_t musinfo; + +// +// S_ParseMusInfo +// Parses MUSINFO lump. +// +void S_ParseMusInfo(const char *mapid) +{ + + if (W_CheckNumForName("MUSINFO") != -1) + { + int num, lumpnum; + int inMap = false; + int load_muslump = -1; + /* musinfo item zero is initialized + * before we reach the parser and it must be + * saved and restored */ + int itemzero = musinfo.items[0]; + + /* don't restart music that is already playing */ + if (mus_playing && + mus_playing->lumpnum == S_music[NUMMUSIC].lumpnum) { + load_muslump = S_music[NUMMUSIC].lumpnum; + } + + memset(&musinfo, 0, sizeof(musinfo)); + musinfo.items[0] = itemzero; + musinfo.current_item = load_muslump; + S_music[NUMMUSIC].lumpnum = load_muslump; + + SC_OpenLump("MUSINFO"); + + while (SC_GetString()) + { + if (inMap || SC_Compare(mapid)) + { + if (!inMap) + { + SC_GetString(); + inMap = true; + } + + if (sc_String[0] == 'E' || sc_String[0] == 'e' || + sc_String[0] == 'M' || sc_String[0] == 'm') + { + break; + } + + // Check number in range + if (M_StrToInt(sc_String, &num) && num > 0 && num < MAX_MUS_ENTRIES) + { + if (SC_GetString()) + { + lumpnum = W_CheckNumForName(sc_String); + + if (lumpnum >= 0) + { + musinfo.items[num] = lumpnum; + } + else + { + lprintf(LO_ERROR, "S_ParseMusInfo: Unknown MUS lump %s", sc_String); + } + } + } + else + { + lprintf(LO_ERROR, "S_ParseMusInfo: Number not in range 1 to %d", MAX_MUS_ENTRIES - 1); + } + } + } + + SC_Close(); + } +} + +void MusInfoThinker(mobj_t *thing) +{ + if (musinfo.mapthing != thing && + thing->subsector->sector == players[displayplayer].mo->subsector->sector) + { + musinfo.lastmapthing = musinfo.mapthing; + musinfo.mapthing = thing; + musinfo.tics = 30; + } +} + +void T_MAPMusic(void) +{ + if (musinfo.tics < 0 || !musinfo.mapthing) + { + return; + } + + if (musinfo.tics > 0) + { + musinfo.tics--; + } + else + { + if (!musinfo.tics && musinfo.lastmapthing != musinfo.mapthing) + { + int arraypt = TIDNUM(musinfo.mapthing); + + if (arraypt >= 0 && arraypt < MAX_MUS_ENTRIES) + { + int lumpnum = musinfo.items[arraypt]; + + if (lumpnum >= 0 && lumpnum < numlumps) + { + S_ChangeMusInfoMusic(lumpnum, true); + } + } + + musinfo.tics = -1; + } + } +} diff --git a/src/s_advsound.h b/src/s_advsound.h new file mode 100644 index 0000000..0db760e --- /dev/null +++ b/src/s_advsound.h @@ -0,0 +1,66 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Support MUSINFO lump (dynamic music changing) + * + *-----------------------------------------------------------------------------*/ + +#ifndef __S_ADVSOUND__ +#define __S_ADVSOUND__ + +#include "p_mobj.h" +#include "sounds.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +// +//MUSINFO lump +// + +#define MAX_MUS_ENTRIES 64 + +typedef struct musinfo_s +{ + mobj_t *mapthing; + mobj_t *lastmapthing; + int tics; + int current_item; + int items[MAX_MUS_ENTRIES]; +} musinfo_t; + +extern musicinfo_t *mus_playing; +extern musinfo_t musinfo; + +void S_ParseMusInfo(const char *mapid); +void MusInfoThinker(mobj_t *thing); +void T_MAPMusic(void); + +#endif diff --git a/src/s_sound.c b/src/s_sound.c new file mode 100644 index 0000000..af1f48a --- /dev/null +++ b/src/s_sound.c @@ -0,0 +1,826 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: Platform-independent sound code + * + *-----------------------------------------------------------------------------*/ + +// killough 3/7/98: modified to allow arbitrary listeners in spy mode +// killough 5/2/98: reindented, removed useless code, beautified + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomstat.h" +#include "s_sound.h" +#include "s_advsound.h" +#include "i_sound.h" +#include "i_system.h" +#include "d_main.h" +#include "r_main.h" +#include "m_random.h" +#include "w_wad.h" +#include "lprintf.h" +#include "sc_man.h" +#include "e6y.h" + +// when to clip out sounds +// Does not fit the large outdoor areas. +#define S_CLIPPING_DIST (1200<>FRACBITS) + +// Adjustable by menu. +#define NORM_PITCH 128 +#define NORM_PRIORITY 64 +#define NORM_SEP 128 +#define S_STEREO_SWING (96<lumpnum = I_GetSfxLumpNum(sfx); + + if (sfx->lumpnum >= 0) + W_LockLumpNum(sfx->lumpnum); + } + } + + // CPhipps - music init reformatted + if (mus_card && !nomusicparm) { + S_SetMusicVolume(musicVolume); + + // no sounds are playing, and they are not mus_paused + mus_paused = 0; + } +} + +void S_Stop(void) +{ + int cnum; + + //jff 1/22/98 skip sound init if sound not enabled + if (snd_card && !nosfxparm) + for (cnum=0 ; cnummusic[0]) + { + int muslump = W_CheckNumForName(gamemapinfo->music); + if (muslump >= 0) + { + musinfo.items[0] = muslump; + S_ChangeMusInfoMusic(muslump, true); + return; + } + // If the mapinfo defined music cannot be found, try the default for the given map. + } + + if (idmusnum!=-1) + mnum = idmusnum; //jff 3/17/98 reload IDMUS music if not -1 + else + if (gamemode == commercial) + mnum = mus_runnin + WRAP(gamemap - 1, NUMMUSIC - mus_runnin); + else + { + static const 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 + WRAP((gameepisode-1)*9 + gamemap-1, mus_runnin - mus_e1m1); + else + mnum = spmus[WRAP(gamemap-1, 9)]; + } + + memset(&musinfo, 0, sizeof(musinfo)); + musinfo.items[0] = -1; + + S_ChangeMusic(mnum, true); +} + +static void S_StartSoundAtVolume(degenmobj_t *origin, int sfx_id, int volume) +{ + int sep, pitch, priority, cnum, is_pickup; + sfxinfo_t *sfx; + + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return; + + is_pickup = sfx_id & PICKUP_SOUND || sfx_id == sfx_oof || (compatibility_level >= prboom_2_compatibility && sfx_id == sfx_noway); // killough 4/25/98 + sfx_id &= ~PICKUP_SOUND; + + if (sfx_id == sfx_None) + return; + + // check for bogus sound # + if (sfx_id < 1 || sfx_id > NUMSFX) + I_Error("S_StartSoundAtVolume: Bad sfx #: %d", sfx_id); + + sfx = &S_sfx[sfx_id]; + + // Initialize sound parameters + if (sfx->link) + { + pitch = sfx->pitch; + priority = sfx->priority; + volume += sfx->volume; + + if (volume < 1) + return; + + if (volume > snd_SfxVolume) + volume = snd_SfxVolume; + } + else + { + pitch = NORM_PITCH; + priority = NORM_PRIORITY; + } + + // Check to see if it is audible, modify the params + // killough 3/7/98, 4/25/98: code rearranged slightly + + if (!origin || (origin == (degenmobj_t*)players[displayplayer].mo && walkcamera.type < 2)) { + sep = NORM_SEP; + volume *= 8; + } else + if (!S_AdjustSoundParams(players[displayplayer].mo, origin, &volume, + &sep, &pitch)) + return; + else + if ( origin->x == players[displayplayer].mo->x && + origin->y == players[displayplayer].mo->y) + 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); + + if (pitch<0) + pitch = 0; + + if (pitch>255) + pitch = 255; + + // kill old sound + for (cnum=0 ; cnumlumpnum < 0 && (sfx->lumpnum = I_GetSfxLumpNum(sfx)) < 0) + return; + + // Assigns the handle to one of the channels in the mix/output buffer. + { // e6y: [Fix] Crash with zero-length sounds. + int h = I_StartSound(sfx_id, cnum, volume, sep, pitch, priority); + if (h != -1) + { + channels[cnum].handle = h; + channels[cnum].pitch = pitch; + } + } +} + +void S_StartSound(void *origin, int sfx_id) +{ + S_StartSoundAtVolume(origin, sfx_id, snd_SfxVolume); +} + +void S_StopSound(void *origin) +{ + int cnum; + + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return; + + for (cnum=0 ; cnumx = mobj->x; + sobj->y = mobj->y; + sobj->z = mobj->z; + channels[cnum].origin = (mobj_t *) sobj; + break; + } + } + } +} + +// +// Stop and resume music, during game PAUSE. +// +void S_PauseSound(void) +{ + //jff 1/22/98 return if music is not enabled + if (!mus_card || nomusicparm) + return; + + if (mus_playing && !mus_paused) + { + I_PauseSong(mus_playing->handle); + mus_paused = true; + } +} + +void S_ResumeSound(void) +{ + //jff 1/22/98 return if music is not enabled + if (!mus_card || nomusicparm) + return; + + if (mus_playing && mus_paused) + { + I_ResumeSong(mus_playing->handle); + mus_paused = false; + } +} + + +// +// Updates music & sounds +// +void S_UpdateSounds(void* listener_p) +{ + mobj_t *listener = (mobj_t*) listener_p; + int cnum; + + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return; + +#ifdef UPDATE_MUSIC + I_UpdateMusic(); +#endif + + for (cnum=0 ; cnumsfxinfo)) + { + if (I_SoundIsPlaying(c->handle)) + { + // initialize parameters + int volume = snd_SfxVolume; + int pitch = c->pitch; // use channel's pitch! + int sep = NORM_SEP; + + if (sfx->link) + { + pitch = sfx->pitch; + volume += sfx->volume; + if (volume < 1) + { + S_StopChannel(cnum); + continue; + } + else + if (volume > snd_SfxVolume) + volume = snd_SfxVolume; + } + + // check non-local sounds for distance clipping + // or modify their params + if (c->origin && listener_p != c->origin) { // killough 3/20/98 + if (!S_AdjustSoundParams(listener, c->origin, + &volume, &sep, &pitch)) + S_StopChannel(cnum); + else + I_UpdateSoundParams(c->handle, volume, sep, pitch); + } + } + else // if channel is allocated but sound has stopped, free it + S_StopChannel(cnum); + } + } +} + + + +void S_SetMusicVolume(int volume) +{ + //jff 1/22/98 return if music is not enabled + if (!mus_card || nomusicparm) + return; + if (volume < 0 || volume > 15) + I_Error("S_SetMusicVolume: Attempt to set music volume at %d", volume); + I_SetMusicVolume(volume); + snd_MusicVolume = volume; +} + + + +void S_SetSfxVolume(int volume) +{ + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return; + if (volume < 0 || volume > 127) + I_Error("S_SetSfxVolume: Attempt to set sfx volume at %d", volume); + snd_SfxVolume = volume; +} + + + +// Starts some music with the music id found in sounds.h. +// +void S_StartMusic(int m_id) +{ + S_ChangeMusic(m_id, false); +} + + + +void S_ChangeMusic(int musicnum, int looping) +{ + musicinfo_t *music; + int music_file_failed; // cournia - if true load the default MIDI music + char* music_filename; // cournia + + // current music which should play + musicnum_current = musicnum; + musinfo.current_item = -1; + S_music[NUMMUSIC].lumpnum = -1; + + //jff 1/22/98 return if music is not enabled + if (!mus_card || nomusicparm) + return; + + if (musicnum <= mus_None || musicnum >= NUMMUSIC) + I_Error("S_ChangeMusic: Bad music number %d", musicnum); + + music = &S_music[musicnum]; + + if (mus_playing == music) + return; + + // shutdown old music + S_StopMusic(); + + // get lumpnum if neccessary + if (!music->lumpnum) + { + char namebuf[9]; + sprintf(namebuf, "d_%s", music->name); + music->lumpnum = W_GetNumForName(namebuf); + } + + music_file_failed = 1; + + // proff_fs - only load when from IWAD + if (lumpinfo[music->lumpnum].source == source_iwad) + { + // cournia - check to see if we can play a higher quality music file + // rather than the default MIDI + music_filename = I_FindFile(S_music_files[musicnum], ""); + if (music_filename) + { + music_file_failed = I_RegisterMusic(music_filename, music); + free(music_filename); + } + } + + if (music_file_failed) + { + //cournia - could not load music file, play default MIDI music + + // load & register it + music->data = W_CacheLumpNum(music->lumpnum); + music->handle = I_RegisterSong(music->data, W_LumpLength(music->lumpnum)); + } + + // play it + I_PlaySong(music->handle, looping); + + mus_playing = music; + + musinfo.current_item = -1; + + // [crispy] MUSINFO value 0 is reserved for the map's default music + if (musinfo.items[0] == -1) + { + musinfo.items[0] = music->lumpnum; + S_music[NUMMUSIC].lumpnum = -1; + } +} + +void S_RestartMusic(void) +{ + if (musinfo.current_item != -1) + { + S_ChangeMusInfoMusic(musinfo.current_item, true); + } + else + { + if (musicnum_current > mus_None && musicnum_current < NUMMUSIC) + { + S_ChangeMusic(musicnum_current, true); + } + } +} + +void S_ChangeMusInfoMusic(int lumpnum, int looping) +{ + musicinfo_t *music; + + if (doSkip) + { + musinfo.current_item = lumpnum; + return; + } + + //jff 1/22/98 return if music is not enabled + if (!mus_card || nomusicparm) + return; + + if (mus_playing && mus_playing->lumpnum == lumpnum) + return; + + music = &S_music[NUMMUSIC]; + + if (music->lumpnum == lumpnum) + return; + + // shutdown old music + S_StopMusic(); + + // save lumpnum + music->lumpnum = lumpnum; + + // load & register it + music->data = W_CacheLumpNum(music->lumpnum); + music->handle = I_RegisterSong(music->data, W_LumpLength(music->lumpnum)); + + // play it + I_PlaySong(music->handle, looping); + + mus_playing = music; + + musinfo.current_item = lumpnum; +} + +void S_StopMusic(void) +{ + //jff 1/22/98 return if music is not enabled + if (!mus_card || nomusicparm) + return; + + if (mus_playing) + { + if (mus_paused) + I_ResumeSong(mus_playing->handle); + + I_StopSong(mus_playing->handle); + I_UnRegisterSong(mus_playing->handle); + if (mus_playing->lumpnum >= 0) + W_UnlockLumpNum(mus_playing->lumpnum); // cph - release the music data + + mus_playing->data = 0; + mus_playing = 0; + } +} + + + +void S_StopChannel(int cnum) +{ + int i; + channel_t *c = &channels[cnum]; + + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return; + + if (c->sfxinfo) + { + // stop the sound playing + if (I_SoundIsPlaying(c->handle)) + I_StopSound(c->handle); + + // check to see + // if other channels are playing the sound + for (i=0 ; isfxinfo == channels[i].sfxinfo) + break; + + c->sfxinfo = 0; + } +} + +// +// Changes volume, stereo-separation, and pitch variables +// from the norm of a sound effect to be played. +// If the sound is not audible, returns a 0. +// Otherwise, modifies parameters and returns 1. +// + +int S_AdjustSoundParams(mobj_t *listener, degenmobj_t *source, + int *vol, int *sep, int *pitch) +{ + fixed_t adx, ady,approx_dist; + angle_t angle; + + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return 0; + + // e6y + // Fix crash when the program wants to S_AdjustSoundParams() for player + // which is not displayplayer and displayplayer was not spawned at the moment. + // It happens in multiplayer demos only. + // + // Stack trace is: + // P_SetupLevel() - P_LoadThings() - P_SpawnMapThing() \ P_SpawnPlayer(players[0]) - + // P_SetupPsprites() - P_BringUpWeapon() - S_StartSound(players[0]->mo, sfx_sawup) - + // S_StartSoundAtVolume() - S_AdjustSoundParams(players[displayplayer]->mo, ...); + // players[displayplayer]->mo is NULL + // + // There is no more crash on e1cmnet3.lmp between e1m2 and e1m3 + // http://competn.doom2.net/pub/compet-n/doom/coop/movies/e1cmnet3.zip + if (!listener) + return 0; + + // calculate the distance to sound origin + // and clip it if necessary + if (walkcamera.type > 1) + { + adx = D_abs(walkcamera.x - source->x); + ady = D_abs(walkcamera.y - source->y); + } + else + { + adx = D_abs(listener->x - source->x); + ady = D_abs(listener->y - source->y); + } + + // From _GG1_ p.428. Appox. eucledian distance fast. + approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1); + + if (!approx_dist) // killough 11/98: handle zero-distance as special case + { + *sep = NORM_SEP; + *vol = snd_SfxVolume; + return *vol > 0; + } + + if (approx_dist > S_CLIPPING_DIST) + return 0; + + // angle of source to listener + angle = R_PointToAngle2(listener->x, listener->y, source->x, source->y); + + if (angle <= listener->angle) + angle += 0xffffffff; + angle -= listener->angle; + angle >>= ANGLETOFINESHIFT; + + // stereo separation + *sep = 128 - (FixedMul(S_STEREO_SWING,finesine[angle])>>FRACBITS); + + // volume calculation + if (approx_dist < S_CLOSE_DIST) + *vol = snd_SfxVolume*8; + else + // distance effect + *vol = (snd_SfxVolume * ((S_CLIPPING_DIST-approx_dist)>>FRACBITS) * 8) + / S_ATTENUATOR; + + return (*vol > 0); +} + +// +// S_getChannel : +// If none available, return -1. Otherwise channel #. +// +// killough 4/25/98: made static, added is_pickup argument + +static int S_getChannel(void *origin, sfxinfo_t *sfxinfo, int is_pickup) +{ + // channel number to use + int cnum; + channel_t *c; + + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return -1; + + // Find an open channel + for (cnum=0; cnumpriority >= sfxinfo->priority) + break; + if (cnum == numChannels) + return -1; // No lower priority. Sorry, Charlie. + else + S_StopChannel(cnum); // Otherwise, kick out lower priority. + } + + c = &channels[cnum]; // channel is decided to be cnum. + c->sfxinfo = sfxinfo; + c->origin = origin; + c->is_pickup = is_pickup; // killough 4/25/98 + return cnum; +} diff --git a/src/s_sound.h b/src/s_sound.h new file mode 100644 index 0000000..99faa0c --- /dev/null +++ b/src/s_sound.h @@ -0,0 +1,104 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * The not so system specific sound interface. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __S_SOUND__ +#define __S_SOUND__ + +#ifdef __GNUG__ +#pragma interface +#endif + +#define MAX_CHANNELS 32 + +// +// 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); + +// Kills all sounds +void S_Stop(void); + +// +// Per level startup code. +// Kills playing sounds at start of level, +// determines music if any, changes music. +// +void S_Start(void); + +// +// Start sound for thing at +// using from sounds.h +// +void S_StartSound(void *origin, int sound_id); + +// killough 4/25/98: mask used to indicate sound origin is player item pickup +#define PICKUP_SOUND (0x8000) + +// Stop sound for thing at +void S_StopSound(void* origin); + +extern int full_sounds; +void S_UnlinkSound(void *origin); + +// Start music using from sounds.h +void S_StartMusic(int music_id); + +// Start music using from sounds.h, and set whether looping +void S_ChangeMusic(int music_id, int looping); +void S_ChangeMusInfoMusic(int lumpnum, int looping); +void S_RestartMusic(void); + +// Stops the music fer sure. +void S_StopMusic(void); + +// Stop and resume music, during game PAUSE. +void S_PauseSound(void); +void S_ResumeSound(void); + +// +// Updates music & sounds +// +void S_UpdateSounds(void* listener); +void S_SetMusicVolume(int volume); +void S_SetSfxVolume(int volume); + +// machine-independent sound params +extern int default_numChannels; +extern int numChannels; + +//jff 3/17/98 holds last IDMUS number, or -1 +extern int idmusnum; + +#endif diff --git a/src/sc_man.c b/src/sc_man.c new file mode 100644 index 0000000..0d49d7d --- /dev/null +++ b/src/sc_man.c @@ -0,0 +1,440 @@ +//************************************************************************** +//** +//** sc_man.c : Heretic 2 : Raven Software, Corp. +//** +//** $RCSfile: sc_man.c,v $ +//** $Revision: 1.3 $ +//** $Date: 96/01/06 03:23:43 $ +//** $Author: bgokey $ +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include +#include "doomtype.h" +#include "w_wad.h" +#include "m_misc.h" +#include "z_zone.h" +#include "lprintf.h" + +#include "sc_man.h" + +// MACROS ------------------------------------------------------------------ + +#define MAX_STRING_SIZE 256 +#define ASCII_COMMENT (';') +#define ASCII_QUOTE (34) + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void CheckOpen(void); +static void OpenScript(void); +static void OpenScriptByName(const char *name); +static void OpenScriptByNum(int lump); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +char *sc_String; +int sc_Number; +int sc_Line; +dboolean sc_End; +dboolean sc_Crossed; +dboolean sc_FileScripts = false; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static char ScriptName[16]; +static int ScriptLump; +static const char *ScriptBuffer; +static const char *ScriptPtr; +static const char *ScriptEndPtr; +static char StringBuffer[MAX_STRING_SIZE]; +static dboolean ScriptOpen = false; +static int ScriptSize; +static dboolean AlreadyGot = false; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// SC_OpenLump +// +// Loads a script (from the WAD files) and prepares it for parsing. +// +//========================================================================== + +void SC_OpenLump(const char *name) +{ + OpenScriptByName(name); +} + +void SC_OpenLumpByNum(int lump) +{ + OpenScriptByNum(lump); +} + +//========================================================================== +// +// OpenScript +// +//========================================================================== + +static void OpenScript(void) +{ + ScriptBuffer = W_CacheLumpNum(ScriptLump); + ScriptSize = W_LumpLength(ScriptLump); + ScriptBuffer = W_CacheLumpNum(ScriptLump); + ScriptSize = W_LumpLength(ScriptLump); + + ScriptPtr = ScriptBuffer; + ScriptEndPtr = ScriptPtr + ScriptSize; + sc_Line = 1; + sc_End = false; + ScriptOpen = true; + sc_String = StringBuffer; + AlreadyGot = false; +} + +static void OpenScriptByName(const char *name) +{ + SC_Close(); + + // Lump script + ScriptLump = W_GetNumForName(name); + strcpy(ScriptName, name); + + OpenScript(); +} + +static void OpenScriptByNum(int lump) +{ + SC_Close(); + + // Lump script + ScriptLump = lump; + strcpy(ScriptName, W_GetLumpInfoByNum(ScriptLump)->name); + + OpenScript(); +} + +//========================================================================== +// +// SC_Close +// +//========================================================================== + +void SC_Close(void) +{ + if (ScriptOpen) + { + W_UnlockLumpNum(ScriptLump); + ScriptOpen = false; + } +} + +//========================================================================== +// +// SC_GetString +// +//========================================================================== + +dboolean SC_GetString(void) +{ + char *text; + dboolean foundToken; + + CheckOpen(); + if (AlreadyGot) + { + AlreadyGot = false; + return true; + } + foundToken = false; + sc_Crossed = false; + if (ScriptPtr >= ScriptEndPtr) + { + sc_End = true; + return false; + } + while (foundToken == false) + { + while (*ScriptPtr <= 32) + { + if (ScriptPtr >= ScriptEndPtr) + { + sc_End = true; + return false; + } + if (*ScriptPtr++ == '\n') + { + sc_Line++; + sc_Crossed = true; + } + } + if (ScriptPtr >= ScriptEndPtr) + { + sc_End = true; + return false; + } + if (*ScriptPtr != ASCII_COMMENT) + { // Found a token + foundToken = true; + } + else + { // Skip comment + while (*ScriptPtr++ != '\n') + { + if (ScriptPtr >= ScriptEndPtr) + { + sc_End = true; + return false; + } + } + sc_Line++; + sc_Crossed = true; + } + } + text = sc_String; + if (*ScriptPtr == ASCII_QUOTE) + { // Quoted string + ScriptPtr++; + while (*ScriptPtr != ASCII_QUOTE) + { + *text++ = *ScriptPtr++; + if (ScriptPtr == ScriptEndPtr || text == &sc_String[MAX_STRING_SIZE - 1]) + { + break; + } + } + ScriptPtr++; + } + else + { // Normal string + while ((*ScriptPtr > 32) && (*ScriptPtr != ASCII_COMMENT)) + { + *text++ = *ScriptPtr++; + if(ScriptPtr == ScriptEndPtr || text == &sc_String[MAX_STRING_SIZE-1]) + { + break; + } + } + } + *text = 0; + return true; +} + +//========================================================================== +// +// SC_MustGetString +// +//========================================================================== + +void SC_MustGetString(void) +{ + if (SC_GetString() == false) + { + SC_ScriptError("Missing string."); + } +} + +//========================================================================== +// +// SC_MustGetStringName +// +//========================================================================== + +void SC_MustGetStringName(const char *name) +{ + SC_MustGetString(); + if (SC_Compare(name) == false) + { + SC_ScriptError(NULL); + } +} + +//========================================================================== +// +// SC_GetNumber +// +//========================================================================== + +dboolean SC_GetNumber(void) +{ + char *stopper; + + CheckOpen(); + if (SC_GetString()) + { + sc_Number = strtol(sc_String, &stopper, 0); + if (*stopper != 0) + { + I_Error("SC_GetNumber: Bad numeric constant \"%s\".\n" + "Script %s, Line %d", sc_String, ScriptName, sc_Line); + } + return true; + } + else + { + return false; + } +} + +//========================================================================== +// +// SC_MustGetNumber +// +//========================================================================== + +void SC_MustGetNumber(void) +{ + if (SC_GetNumber() == false) + { + SC_ScriptError("Missing integer."); + } +} + +//========================================================================== +// +// SC_UnGet +// +// Assumes there is a valid string in sc_String. +// +//========================================================================== + +void SC_UnGet(void) +{ + AlreadyGot = true; +} + +//========================================================================== +// +// SC_Check +// +// Returns true if another token is on the current line. +// +//========================================================================== + + +dboolean SC_Check(void) +{ + const char *text; + + CheckOpen(); + text = ScriptPtr; + if (text >= ScriptEndPtr) + { + return false; + } + while (*text <= 32) + { + if (*text == '\n') + { + return false; + } + text++; + if(text == ScriptEndPtr) + { + return false; + } + } + if (*text == ASCII_COMMENT) + { + return false; + } + return true; +} + + +//========================================================================== +// +// SC_MatchString +// +// Returns the index of the first match to sc_String from the passed +// array of strings, or -1 if not found. +// +//========================================================================== + +int SC_MatchString(const char **strings) +{ + int i; + + for (i = 0; *strings != NULL; i++) + { + if (SC_Compare(*strings++)) + { + return i; + } + } + return -1; +} + +//========================================================================== +// +// SC_MustMatchString +// +//========================================================================== + +int SC_MustMatchString(const char **strings) +{ + int i; + + i = SC_MatchString(strings); + if (i == -1) + { + SC_ScriptError(NULL); + } + return i; +} + +//========================================================================== +// +// SC_Compare +// +//========================================================================== + +dboolean SC_Compare(const char *text) +{ + if (strcasecmp(text, sc_String) == 0) + { + return true; + } + return false; +} + +//========================================================================== +// +// SC_ScriptError +// +//========================================================================== + +void SC_ScriptError(const char *message) +{ + if (message == NULL) + { + message = "Bad syntax."; + } + I_Error("Script error, \"%s\" line %d: %s", ScriptName, sc_Line, message); +} + +//========================================================================== +// +// CheckOpen +// +//========================================================================== + +static void CheckOpen(void) +{ + if (ScriptOpen == false) + { + I_Error("SC_ call before SC_Open()."); + } +} diff --git a/src/sc_man.h b/src/sc_man.h new file mode 100644 index 0000000..c31267c --- /dev/null +++ b/src/sc_man.h @@ -0,0 +1,26 @@ +#ifndef __SC_MAN__ +#define __SC_MAN__ + +void SC_OpenLump(const char *name); +void SC_OpenLumpByNum(int lump); +void SC_Close(void); +dboolean SC_GetString(void); +void SC_MustGetString(void); +void SC_MustGetStringName(const char *name); +dboolean SC_GetNumber(void); +void SC_MustGetNumber(void); +void SC_UnGet(void); +dboolean SC_Check(void); +dboolean SC_Compare(const char *text); +int SC_MatchString(const char **strings); +int SC_MustMatchString(const char **strings); +void SC_ScriptError(const char *message); + +extern char *sc_String; +extern int sc_Number; +extern int sc_Line; +extern dboolean sc_End; +extern dboolean sc_Crossed; +extern dboolean sc_FileScripts; + +#endif // __SC_MAN__ diff --git a/src/scanner.cpp b/src/scanner.cpp new file mode 100644 index 0000000..3725e04 --- /dev/null +++ b/src/scanner.cpp @@ -0,0 +1,699 @@ +// Copyright (c) 2010, Braden "Blzut3" Obrzut +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include +#include + +#include "scanner.h" + + +#ifdef _WIN32 +# define strcmpnocase stricmp +#else +# include +# define strcmpnocase strcasecmp +#endif + + +const char* const Scanner::TokenNames[TK_NumSpecialTokens] = +{ + "Identifier", + "String Constant", + "Integer Constant", + "Float Constant", + "Boolean Constant", + "Logical And", + "Logical Or", + "Equals", + "Not Equals", + "Greater Than or Equals" + "Less Than or Equals", + "Left Shift", + "Right Shift" +}; + +static void __ScannerStandardError(const char* message, ...) +{ + va_list list; + va_start(list, message); + vfprintf(stderr, message, list); + va_end(list); +} +void (*Scanner::error)(const char*, ...) = __ScannerStandardError; + +Scanner::Scanner(const char* data, int length) : line(1), lineStart(0), logicalPosition(0), tokenLine(1), tokenLinePosition(0), scanPos(0), needNext(true) +{ + if(length == -1) + length = strlen(data); + this->length = length; + this->data = new char[length]; + memcpy(this->data, data, length); + string = NULL; + + CheckForWhitespace(); +} + +Scanner::~Scanner() +{ + if (string != NULL) delete[] string; + delete[] data; +} + +void Scanner::SetString(char **ptr, const char *start, unsigned int length) +{ + if (length == -1) + length = strlen(start); + if (*ptr != NULL) free(*ptr); + *ptr = (char*)malloc(length + 1); + memcpy(*ptr, start, length); + (*ptr)[length] = 0; +} + +void Scanner::CheckForWhitespace() +{ + int comment = 0; // 1 = till next new line, 2 = till end block + while(scanPos < length) + { + char cur = data[scanPos]; + char next = scanPos+1 < length ? data[scanPos+1] : 0; + if(comment == 2) + { + if(cur != '*' || next != '/') + { + if(cur == '\n' || cur == '\r') + { + scanPos++; + if(comment == 1) + comment = 0; + + // Do a quick check for Windows style new line + if(cur == '\r' && next == '\n') + scanPos++; + IncrementLine(); + } + else + scanPos++; + } + else + { + comment = 0; + scanPos += 2; + } + continue; + } + + if(cur == ' ' || cur == '\t' || cur == 0) + scanPos++; + else if(cur == '\n' || cur == '\r') + { + scanPos++; + if(comment == 1) + comment = 0; + + // Do a quick check for Windows style new line + if(cur == '\r' && next == '\n') + scanPos++; + IncrementLine(); + //CheckForMeta(); + } + else if(cur == '/' && comment == 0) + { + switch(next) + { + case '/': + comment = 1; + break; + case '*': + comment = 2; + break; + default: + return; + } + scanPos += 2; + } + else + { + if(comment == 0) + return; + else + scanPos++; + } + } +} + +bool Scanner::CheckToken(char token) +{ + if(needNext) + { + if(!GetNextToken(false)) + return false; + } + + // An int can also be a float. + if(nextState.token == token || (nextState.token == TK_IntConst && token == TK_FloatConst)) + { + needNext = true; + ExpandState(); + return true; + } + needNext = false; + return false; +} + +void Scanner::ExpandState() +{ + logicalPosition = scanPos; + CheckForWhitespace(); + + SetString(&string, nextState.string, -1); + number = nextState.number; + decimal = nextState.decimal; + boolean = nextState.boolean; + token = nextState.token; + tokenLine = nextState.tokenLine; + tokenLinePosition = nextState.tokenLinePosition; +} + +void Scanner::SaveState(Scanner &savedstate) +{ + // This saves the entire parser state except for the data pointer. + if (savedstate.string != NULL) free(savedstate.string); + if (savedstate.nextState.string != NULL) free(savedstate.nextState.string); + memcpy(&savedstate, this, sizeof(*this)); + savedstate.string = strdup(string); + savedstate.nextState.string = strdup(nextState.string); + savedstate.data = NULL; +} + +void Scanner::RestoreState(Scanner &savedstate) +{ + if (savedstate.data == NULL) + { + char *saveddata = data; + savedstate.SaveState(*this); + data = saveddata; + } +} + +bool Scanner::GetNextToken(bool expandState) +{ + if(!needNext) + { + needNext = true; + if(expandState) + ExpandState(); + return true; + } + + nextState.tokenLine = line; + nextState.tokenLinePosition = scanPos - lineStart; + nextState.token = TK_NoToken; + if(scanPos >= length) + { + if(expandState) + ExpandState(); + return false; + } + + int start = scanPos; + int end = scanPos; + int integerBase = 10; + bool floatHasDecimal = false; + bool floatHasExponent = false; + bool stringFinished = false; // Strings are the only things that can have 0 length tokens. + + char cur = data[scanPos++]; + // Determine by first character + if(cur == '_' || (cur >= 'A' && cur <= 'Z') || (cur >= 'a' && cur <= 'z')) + nextState.token = TK_Identifier; + else if(cur >= '0' && cur <= '9') + { + if(cur == '0') + integerBase = 8; + nextState.token = TK_IntConst; + } + else if(cur == '.') + { + floatHasDecimal = true; + nextState.token = TK_FloatConst; + } + else if(cur == '"') + { + end = ++start; // Move the start up one character so we don't have to trim it later. + nextState.token = TK_StringConst; + } + else + { + end = scanPos; + nextState.token = cur; + + // Now check for operator tokens + if(scanPos < length) + { + char next = data[scanPos]; + if(cur == '&' && next == '&') + nextState.token = TK_AndAnd; + else if(cur == '|' && next == '|') + nextState.token = TK_OrOr; + else if(cur == '<' && next == '<') + nextState.token = TK_ShiftLeft; + else if(cur == '>' && next == '>') + nextState.token = TK_ShiftRight; + //else if(cur == '#' && next == '#') + // nextState.token = TK_MacroConcat; + else if(next == '=') + { + switch(cur) + { + case '=': + nextState.token = TK_EqEq; + break; + case '!': + nextState.token = TK_NotEq; + break; + case '>': + nextState.token = TK_GtrEq; + break; + case '<': + nextState.token = TK_LessEq; + break; + default: + break; + } + } + + if(nextState.token != cur) + { + scanPos++; + end = scanPos; + } + } + } + + if(start == end) + { + while(scanPos < length) + { + cur = data[scanPos]; + switch(nextState.token) + { + default: + break; + case TK_Identifier: + if(cur != '_' && (cur < 'A' || cur > 'Z') && (cur < 'a' || cur > 'z') && (cur < '0' || cur > '9')) + end = scanPos; + break; + case TK_IntConst: + if(cur == '.' || (scanPos-1 != start && cur == 'e')) + nextState.token = TK_FloatConst; + else if((cur == 'x' || cur == 'X') && scanPos-1 == start) + { + integerBase = 16; + break; + } + else + { + switch(integerBase) + { + default: + if(cur < '0' || cur > '9') + end = scanPos; + break; + case 8: + if(cur < '0' || cur > '7') + end = scanPos; + break; + case 16: + if((cur < '0' || cur > '9') && (cur < 'A' || cur > 'F') && (cur < 'a' || cur > 'f')) + end = scanPos; + break; + } + break; + } + case TK_FloatConst: + if(cur < '0' || cur > '9') + { + if(!floatHasDecimal && cur == '.') + { + floatHasDecimal = true; + break; + } + else if(!floatHasExponent && cur == 'e') + { + floatHasDecimal = true; + floatHasExponent = true; + if(scanPos+1 < length) + { + char next = data[scanPos+1]; + if((next < '0' || next > '9') && next != '+' && next != '-') + end = scanPos; + else + scanPos++; + } + break; + } + end = scanPos; + } + break; + case TK_StringConst: + if(cur == '"') + { + stringFinished = true; + end = scanPos; + scanPos++; + } + else if(cur == '\\') + scanPos++; // Will add two since the loop automatically adds one + break; + } + if(start == end && !stringFinished) + scanPos++; + else + break; + } + } + + if(end-start > 0 || stringFinished) + { + SetString(&nextState.string, data+start, end-start); + if(nextState.token == TK_FloatConst) + { + nextState.decimal = atof(nextState.string); + nextState.number = static_cast (nextState.decimal); + nextState.boolean = (nextState.number != 0); + } + else if(nextState.token == TK_IntConst) + { + nextState.number = strtol(nextState.string, NULL, integerBase); + nextState.decimal = nextState.number; + nextState.boolean = (nextState.number != 0); + } + else if(nextState.token == TK_Identifier) + { + // Identifiers should be case insensitive. + char *p = nextState.string; + while (*p) + { + *p = tolower(*p); + p++; + } + // Check for a boolean constant. + if(strcmp(nextState.string, "true") == 0) + { + nextState.token = TK_BoolConst; + nextState.boolean = true; + } + else if (strcmp(nextState.string, "false") == 0) + { + nextState.token = TK_BoolConst; + nextState.boolean = false; + } + } + else if(nextState.token == TK_StringConst) + { + Unescape(nextState.string); + } + if(expandState) + ExpandState(); + return true; + } + nextState.token = TK_NoToken; + if(expandState) + ExpandState(); + return false; +} + +void Scanner::IncrementLine() +{ + line++; + lineStart = scanPos; +} + +void Scanner::Error(int token) +{ + if (token < TK_NumSpecialTokens && this->token >= TK_Identifier && this->token < TK_NumSpecialTokens) + error("%d:%d:Expected '%s' but got '%s' instead.", GetLine(), GetLinePos(), TokenNames[token], TokenNames[this->token]); + else if (token < TK_NumSpecialTokens && this->token >= TK_NumSpecialTokens) + error("%d:%d:Expected '%s' but got '%c' instead.", GetLine(), GetLinePos(), TokenNames[token], this->token); + else if (token < TK_NumSpecialTokens && this->token == TK_NoToken) + error("%d:%d:Expected '%s'", GetLine(), GetLinePos(), TokenNames[token]); + else if (token >= TK_NumSpecialTokens && this->token >= TK_Identifier && this->token < TK_NumSpecialTokens) + error("%d:%d:Expected '%c' but got '%s' instead.", GetLine(), GetLinePos(), token, TokenNames[this->token]); + else + error("%d:%d:Expected '%c' but got '%c' instead.", GetLine(), GetLinePos(), token, this->token); +} + +void Scanner::Error(const char *mustget) +{ + if (token < TK_NumSpecialTokens && this->token < TK_NumSpecialTokens) + error("%d:%d:Expected '%s' but got '%s' instead.", GetLine(), GetLinePos(), mustget, TokenNames[this->token]); + else + error("%d:%d:Expected '%s' but got '%c' instead.", GetLine(), GetLinePos(), mustget, this->token); +} + +void Scanner::ErrorF(const char *msg, ...) +{ + char buffer[1024]; + va_list ap; + va_start(ap, msg); + vsnprintf(buffer, 1024, msg, ap); + va_end(ap); + error("%d:%d:%s.", GetLine(), GetLinePos(), buffer); +} + +void Scanner::MustGetToken(char token) +{ + if(!CheckToken(token)) + { + ExpandState(); + Error(token); + } +} + +void Scanner::MustGetIdentifier(const char *ident) +{ + if (!CheckToken(TK_Identifier) || strcmpnocase(string, ident)) + { + Error(ident); + return; + } +} + +// Convenience helpers that parse an entire number including a leading minus or plus sign +bool Scanner::ScanInteger() +{ + bool neg = false; + if (!GetNextToken()) + { + return false; + } + if (token == '-') + { + if (!GetNextToken()) + { + return false; + } + neg = true; + } + else if (token == '+') + { + if (!GetNextToken()) + { + return false; + } + } + if (token != TK_IntConst) + { + return false; + } + if (neg) + { + number = -number; + decimal = -decimal; + } + return true; +} + +bool Scanner::ScanFloat() +{ + bool neg = false; + if (!GetNextToken()) + { + return false; + } + if (token == '-') + { + if (!GetNextToken()) + { + return false; + } + neg = true; + } + else if (token == '+') + { + if (!GetNextToken()) + { + return false; + } + } + if (token != TK_IntConst && token != TK_FloatConst) + { + return false; + } + if (neg) + { + number = -number; + decimal = -decimal; + } + return true; +} + +bool Scanner::CheckInteger() +{ + Scanner savedstate; + SaveState(savedstate); + bool res = ScanInteger(); + if (!res) RestoreState(savedstate); + return res; +} + +bool Scanner::CheckFloat() +{ + Scanner savedstate; + SaveState(savedstate); + bool res = ScanFloat(); + if (!res) RestoreState(savedstate); + return res; +} + +void Scanner::MustGetInteger() +{ + if (!ScanInteger()) Error(TK_IntConst); +} + +void Scanner::MustGetFloat() +{ + if (!ScanFloat()) Error(TK_FloatConst); +} + + +bool Scanner::TokensLeft() const +{ + return scanPos < length; +} + +// This is taken from ZDoom's strbin function which can do a lot more than just unescaping backslashes and quotation marks. +void Scanner::Unescape(char *str) +{ + char *start = str; + char *p = str, c; + int i; + + while ((c = *p++)) { + if (c != '\\') { + *str++ = c; + } + else { + switch (*p) { + case 'a': + *str++ = '\a'; + break; + case 'b': + *str++ = '\b'; + break; + case 'f': + *str++ = '\f'; + break; + case 'n': + *str++ = '\n'; + break; + case 't': + *str++ = '\t'; + break; + case 'r': + *str++ = '\r'; + break; + case 'v': + *str++ = '\v'; + break; + case '?': + *str++ = '\?'; + break; + case '\n': + break; + case 'x': + case 'X': + c = 0; + for (i = 0; i < 2; i++) + { + p++; + if (*p >= '0' && *p <= '9') + c = (c << 4) + *p - '0'; + else if (*p >= 'a' && *p <= 'f') + c = (c << 4) + 10 + *p - 'a'; + else if (*p >= 'A' && *p <= 'F') + c = (c << 4) + 10 + *p - 'A'; + else + { + p--; + break; + } + } + *str++ = c; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + c = *p - '0'; + for (i = 0; i < 2; i++) + { + p++; + if (*p >= '0' && *p <= '7') + c = (c << 3) + *p - '0'; + else + { + p--; + break; + } + } + *str++ = c; + break; + default: + *str++ = *p; + break; + } + p++; + } + } + *str = 0; +} diff --git a/src/scanner.h b/src/scanner.h new file mode 100644 index 0000000..8d35777 --- /dev/null +++ b/src/scanner.h @@ -0,0 +1,140 @@ +// Copyright (c) 2010, Braden "Blzut3" Obrzut +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef __SCANNER_H__ +#define __SCANNER_H__ + +//#include + +enum +{ + TK_Identifier, // Ex: SomeIdentifier + TK_StringConst, // Ex: "Some String" + TK_IntConst, // Ex: 27 + TK_FloatConst, // Ex: 1.5 + TK_BoolConst, // Ex: true + TK_AndAnd, // && + TK_OrOr, // || + TK_EqEq, // == + TK_NotEq, // != + TK_GtrEq, // >= + TK_LessEq, // <= + TK_ShiftLeft, // << + TK_ShiftRight, // >> + + TK_NumSpecialTokens, + + TK_NoToken = -1 +}; + +struct ParserState +{ + char *string; + int number; + double decimal; + bool boolean; + char token; + unsigned int tokenLine; + unsigned int tokenLinePosition; + + ParserState() + { + string = NULL; + } + ~ParserState() + { + if (string != NULL) free(string); + } +}; + +class Scanner +{ + public: + Scanner(const char* data, int length=-1); + ~Scanner(); + + void SetString(char **ptr, const char *src, unsigned int length); + void CheckForWhitespace(); + bool CheckToken(char token); + bool CheckInteger(); + bool CheckFloat(); + void MustGetInteger(); + void MustGetFloat(); + void ExpandState(); + int GetLine() const { return tokenLine; } + int GetLinePos() const { return tokenLinePosition; } + bool GetNextToken(bool expandState=true); + void MustGetToken(char token); + void MustGetIdentifier(const char *ident); + bool TokensLeft() const; + void Error(int token); + void Error(const char *mustget); + void ErrorF(const char *msg, ...); + void Unget() { needNext = true; } + static void SetErrorCallback(void (*cb)(const char*, ...)) { error = cb; } + + static void Unescape(char *str); + + static const char* const TokenNames[TK_NumSpecialTokens]; + + char *string; + int number; + double decimal; + bool boolean; + char token; + + protected: + Scanner() + { + data = NULL; + string = NULL; + nextState.string = NULL; + } + + void IncrementLine(); + void SaveState(Scanner &saved); + void RestoreState(Scanner &savd); + bool ScanInteger(); + bool ScanFloat(); + + private: + ParserState nextState; + + char* data; + unsigned int length; + + unsigned int line; + unsigned int lineStart; + unsigned int logicalPosition; + unsigned int tokenLine; + unsigned int tokenLinePosition; + unsigned int scanPos; + + bool needNext; // If checkToken returns false this will be false. + + static void (*error)(const char* message, ...); +}; + +#endif /* __SCANNER_H__ */ diff --git a/src/sounds.c b/src/sounds.c new file mode 100644 index 0000000..b1e600c --- /dev/null +++ b/src/sounds.c @@ -0,0 +1,457 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Created by a sound utility. + * Kept as a sample, DOOM2 sounds. + * + *-----------------------------------------------------------------------------*/ + +// killough 5/3/98: reformatted + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomtype.h" +#include "sounds.h" + +// +// Information about all the music +// + +musicinfo_t S_music[] = { + { 0 }, + { "e1m1", 0 }, + { "e1m2", 0 }, + { "e1m3", 0 }, + { "e1m4", 0 }, + { "e1m5", 0 }, + { "e1m6", 0 }, + { "e1m7", 0 }, + { "e1m8", 0 }, + { "e1m9", 0 }, + { "e2m1", 0 }, + { "e2m2", 0 }, + { "e2m3", 0 }, + { "e2m4", 0 }, + { "e2m5", 0 }, + { "e2m6", 0 }, + { "e2m7", 0 }, + { "e2m8", 0 }, + { "e2m9", 0 }, + { "e3m1", 0 }, + { "e3m2", 0 }, + { "e3m3", 0 }, + { "e3m4", 0 }, + { "e3m5", 0 }, + { "e3m6", 0 }, + { "e3m7", 0 }, + { "e3m8", 0 }, + { "e3m9", 0 }, + { "inter", 0 }, + { "intro", 0 }, + { "bunny", 0 }, + { "victor", 0 }, + { "introa", 0 }, + { "runnin", 0 }, + { "stalks", 0 }, + { "countd", 0 }, + { "betwee", 0 }, + { "doom", 0 }, + { "the_da", 0 }, + { "shawn", 0 }, + { "ddtblu", 0 }, + { "in_cit", 0 }, + { "dead", 0 }, + { "stlks2", 0 }, + { "theda2", 0 }, + { "doom2", 0 }, + { "ddtbl2", 0 }, + { "runni2", 0 }, + { "dead2", 0 }, + { "stlks3", 0 }, + { "romero", 0 }, + { "shawn2", 0 }, + { "messag", 0 }, + { "count2", 0 }, + { "ddtbl3", 0 }, + { "ampie", 0 }, + { "theda3", 0 }, + { "adrian", 0 }, + { "messg2", 0 }, + { "romer2", 0 }, + { "tense", 0 }, + { "shawn3", 0 }, + { "openin", 0 }, + { "evil", 0 }, + { "ultima", 0 }, + { "read_m", 0 }, + { "dm2ttl", 0 }, + { "dm2int", 0 }, + + // custom music from MUSINFO lump + { "musinfo", 0 } +}; + + +// +// Information about all the sfx +// + +sfxinfo_t S_sfx[] = { + // S_sfx[0] needs to be a dummy for odd reasons. + { "none", false, 0, 0, -1, -1, 0 }, + + { "pistol", false, 64, 0, -1, -1, 0 }, + { "shotgn", false, 64, 0, -1, -1, 0 }, + { "sgcock", false, 64, 0, -1, -1, 0 }, + { "dshtgn", false, 64, 0, -1, -1, 0 }, + { "dbopn", false, 64, 0, -1, -1, 0 }, + { "dbcls", false, 64, 0, -1, -1, 0 }, + { "dbload", false, 64, 0, -1, -1, 0 }, + { "plasma", false, 64, 0, -1, -1, 0 }, + { "bfg", false, 64, 0, -1, -1, 0 }, + { "sawup", false, 64, 0, -1, -1, 0 }, + { "sawidl", false, 118, 0, -1, -1, 0 }, + { "sawful", false, 64, 0, -1, -1, 0 }, + { "sawhit", false, 64, 0, -1, -1, 0 }, + { "rlaunc", false, 64, 0, -1, -1, 0 }, + { "rxplod", false, 70, 0, -1, -1, 0 }, + { "firsht", false, 70, 0, -1, -1, 0 }, + { "firxpl", false, 70, 0, -1, -1, 0 }, + { "pstart", false, 100, 0, -1, -1, 0 }, + { "pstop", false, 100, 0, -1, -1, 0 }, + { "doropn", false, 100, 0, -1, -1, 0 }, + { "dorcls", false, 100, 0, -1, -1, 0 }, + { "stnmov", false, 119, 0, -1, -1, 0 }, + { "swtchn", false, 78, 0, -1, -1, 0 }, + { "swtchx", false, 78, 0, -1, -1, 0 }, + { "plpain", false, 96, 0, -1, -1, 0 }, + { "dmpain", false, 96, 0, -1, -1, 0 }, + { "popain", false, 96, 0, -1, -1, 0 }, + { "vipain", false, 96, 0, -1, -1, 0 }, + { "mnpain", false, 96, 0, -1, -1, 0 }, + { "pepain", false, 96, 0, -1, -1, 0 }, + { "slop", false, 78, 0, -1, -1, 0 }, + { "itemup", true, 78, 0, -1, -1, 0 }, + { "wpnup", true, 78, 0, -1, -1, 0 }, + { "oof", false, 96, 0, -1, -1, 0 }, + { "telept", false, 32, 0, -1, -1, 0 }, + { "posit1", true, 98, 0, -1, -1, 0 }, + { "posit2", true, 98, 0, -1, -1, 0 }, + { "posit3", true, 98, 0, -1, -1, 0 }, + { "bgsit1", true, 98, 0, -1, -1, 0 }, + { "bgsit2", true, 98, 0, -1, -1, 0 }, + { "sgtsit", true, 98, 0, -1, -1, 0 }, + { "cacsit", true, 98, 0, -1, -1, 0 }, + { "brssit", true, 94, 0, -1, -1, 0 }, + { "cybsit", true, 92, 0, -1, -1, 0 }, + { "spisit", true, 90, 0, -1, -1, 0 }, + { "bspsit", true, 90, 0, -1, -1, 0 }, + { "kntsit", true, 90, 0, -1, -1, 0 }, + { "vilsit", true, 90, 0, -1, -1, 0 }, + { "mansit", true, 90, 0, -1, -1, 0 }, + { "pesit", true, 90, 0, -1, -1, 0 }, + { "sklatk", false, 70, 0, -1, -1, 0 }, + { "sgtatk", false, 70, 0, -1, -1, 0 }, + { "skepch", false, 70, 0, -1, -1, 0 }, + { "vilatk", false, 70, 0, -1, -1, 0 }, + { "claw", false, 70, 0, -1, -1, 0 }, + { "skeswg", false, 70, 0, -1, -1, 0 }, + { "pldeth", false, 32, 0, -1, -1, 0 }, + { "pdiehi", false, 32, 0, -1, -1, 0 }, + { "podth1", false, 70, 0, -1, -1, 0 }, + { "podth2", false, 70, 0, -1, -1, 0 }, + { "podth3", false, 70, 0, -1, -1, 0 }, + { "bgdth1", false, 70, 0, -1, -1, 0 }, + { "bgdth2", false, 70, 0, -1, -1, 0 }, + { "sgtdth", false, 70, 0, -1, -1, 0 }, + { "cacdth", false, 70, 0, -1, -1, 0 }, + { "skldth", false, 70, 0, -1, -1, 0 }, + { "brsdth", false, 32, 0, -1, -1, 0 }, + { "cybdth", false, 32, 0, -1, -1, 0 }, + { "spidth", false, 32, 0, -1, -1, 0 }, + { "bspdth", false, 32, 0, -1, -1, 0 }, + { "vildth", false, 32, 0, -1, -1, 0 }, + { "kntdth", false, 32, 0, -1, -1, 0 }, + { "pedth", false, 32, 0, -1, -1, 0 }, + { "skedth", false, 32, 0, -1, -1, 0 }, + { "posact", true, 120, 0, -1, -1, 0 }, + { "bgact", true, 120, 0, -1, -1, 0 }, + { "dmact", true, 120, 0, -1, -1, 0 }, + { "bspact", true, 100, 0, -1, -1, 0 }, + { "bspwlk", true, 100, 0, -1, -1, 0 }, + { "vilact", true, 100, 0, -1, -1, 0 }, + { "noway", false, 78, 0, -1, -1, 0 }, + { "barexp", false, 60, 0, -1, -1, 0 }, + { "punch", false, 64, 0, -1, -1, 0 }, + { "hoof", false, 70, 0, -1, -1, 0 }, + { "metal", false, 70, 0, -1, -1, 0 }, + { "chgun", false, 64, &S_sfx[sfx_pistol], 150, 0, 0 }, + { "tink", false, 60, 0, -1, -1, 0 }, + { "bdopn", false, 100, 0, -1, -1, 0 }, + { "bdcls", false, 100, 0, -1, -1, 0 }, + { "itmbk", false, 100, 0, -1, -1, 0 }, + { "flame", false, 32, 0, -1, -1, 0 }, + { "flamst", false, 32, 0, -1, -1, 0 }, + { "getpow", false, 60, 0, -1, -1, 0 }, + { "bospit", false, 70, 0, -1, -1, 0 }, + { "boscub", false, 70, 0, -1, -1, 0 }, + { "bossit", false, 70, 0, -1, -1, 0 }, + { "bospn", false, 70, 0, -1, -1, 0 }, + { "bosdth", false, 70, 0, -1, -1, 0 }, + { "manatk", false, 70, 0, -1, -1, 0 }, + { "mandth", false, 70, 0, -1, -1, 0 }, + { "sssit", false, 70, 0, -1, -1, 0 }, + { "ssdth", false, 70, 0, -1, -1, 0 }, + { "keenpn", false, 70, 0, -1, -1, 0 }, + { "keendt", false, 70, 0, -1, -1, 0 }, + { "skeact", false, 70, 0, -1, -1, 0 }, + { "skesit", false, 70, 0, -1, -1, 0 }, + { "skeatk", false, 70, 0, -1, -1, 0 }, + { "radio", false, 60, 0, -1, -1, 0 }, + + // killough 11/98: dog sounds + { "dgsit", false, 98, 0, -1, -1, 0 }, + { "dgatk", false, 70, 0, -1, -1, 0 }, + { "dgact", false, 120, 0, -1, -1, 0 }, + { "dgdth", false, 70, 0, -1, -1, 0 }, + { "dgpain", false, 96, 0, -1, -1, 0 }, + + //e6y + { "secret", false, 60, 0, -1, -1, 0 }, + { "gibdth", false, 60, 0, -1, -1, 0 }, + // Everything from here up to 500 is reserved for future use. + + // Free slots for DEHEXTRA. Priorities should be overridden by user. + // There is a gap present to accomodate Eternity Engine - see their commit + // @ https://github.com/team-eternity/eternity/commit/b8fb8f71 - which means + // I must use desginated initializers, or else supply an exact number of dummy + // entries to pad it out. Not sure which would be uglier to maintain. -SH + [500] = { "fre000", false, 127, 0, -1, -1, 0 }, + [501] = { "fre001", false, 127, 0, -1, -1, 0 }, + [502] = { "fre002", false, 127, 0, -1, -1, 0 }, + [503] = { "fre003", false, 127, 0, -1, -1, 0 }, + [504] = { "fre004", false, 127, 0, -1, -1, 0 }, + [505] = { "fre005", false, 127, 0, -1, -1, 0 }, + [506] = { "fre006", false, 127, 0, -1, -1, 0 }, + [507] = { "fre007", false, 127, 0, -1, -1, 0 }, + [508] = { "fre008", false, 127, 0, -1, -1, 0 }, + [509] = { "fre009", false, 127, 0, -1, -1, 0 }, + [510] = { "fre010", false, 127, 0, -1, -1, 0 }, + [511] = { "fre011", false, 127, 0, -1, -1, 0 }, + [512] = { "fre012", false, 127, 0, -1, -1, 0 }, + [513] = { "fre013", false, 127, 0, -1, -1, 0 }, + [514] = { "fre014", false, 127, 0, -1, -1, 0 }, + [515] = { "fre015", false, 127, 0, -1, -1, 0 }, + [516] = { "fre016", false, 127, 0, -1, -1, 0 }, + [517] = { "fre017", false, 127, 0, -1, -1, 0 }, + [518] = { "fre018", false, 127, 0, -1, -1, 0 }, + [519] = { "fre019", false, 127, 0, -1, -1, 0 }, + [520] = { "fre020", false, 127, 0, -1, -1, 0 }, + [521] = { "fre021", false, 127, 0, -1, -1, 0 }, + [522] = { "fre022", false, 127, 0, -1, -1, 0 }, + [523] = { "fre023", false, 127, 0, -1, -1, 0 }, + [524] = { "fre024", false, 127, 0, -1, -1, 0 }, + [525] = { "fre025", false, 127, 0, -1, -1, 0 }, + [526] = { "fre026", false, 127, 0, -1, -1, 0 }, + [527] = { "fre027", false, 127, 0, -1, -1, 0 }, + [528] = { "fre028", false, 127, 0, -1, -1, 0 }, + [529] = { "fre029", false, 127, 0, -1, -1, 0 }, + [530] = { "fre030", false, 127, 0, -1, -1, 0 }, + [531] = { "fre031", false, 127, 0, -1, -1, 0 }, + [532] = { "fre032", false, 127, 0, -1, -1, 0 }, + [533] = { "fre033", false, 127, 0, -1, -1, 0 }, + [534] = { "fre034", false, 127, 0, -1, -1, 0 }, + [535] = { "fre035", false, 127, 0, -1, -1, 0 }, + [536] = { "fre036", false, 127, 0, -1, -1, 0 }, + [537] = { "fre037", false, 127, 0, -1, -1, 0 }, + [538] = { "fre038", false, 127, 0, -1, -1, 0 }, + [539] = { "fre039", false, 127, 0, -1, -1, 0 }, + [540] = { "fre040", false, 127, 0, -1, -1, 0 }, + [541] = { "fre041", false, 127, 0, -1, -1, 0 }, + [542] = { "fre042", false, 127, 0, -1, -1, 0 }, + [543] = { "fre043", false, 127, 0, -1, -1, 0 }, + [544] = { "fre044", false, 127, 0, -1, -1, 0 }, + [545] = { "fre045", false, 127, 0, -1, -1, 0 }, + [546] = { "fre046", false, 127, 0, -1, -1, 0 }, + [547] = { "fre047", false, 127, 0, -1, -1, 0 }, + [548] = { "fre048", false, 127, 0, -1, -1, 0 }, + [549] = { "fre049", false, 127, 0, -1, -1, 0 }, + [550] = { "fre050", false, 127, 0, -1, -1, 0 }, + [551] = { "fre051", false, 127, 0, -1, -1, 0 }, + [552] = { "fre052", false, 127, 0, -1, -1, 0 }, + [553] = { "fre053", false, 127, 0, -1, -1, 0 }, + [554] = { "fre054", false, 127, 0, -1, -1, 0 }, + [555] = { "fre055", false, 127, 0, -1, -1, 0 }, + [556] = { "fre056", false, 127, 0, -1, -1, 0 }, + [557] = { "fre057", false, 127, 0, -1, -1, 0 }, + [558] = { "fre058", false, 127, 0, -1, -1, 0 }, + [559] = { "fre059", false, 127, 0, -1, -1, 0 }, + [560] = { "fre060", false, 127, 0, -1, -1, 0 }, + [561] = { "fre061", false, 127, 0, -1, -1, 0 }, + [562] = { "fre062", false, 127, 0, -1, -1, 0 }, + [563] = { "fre063", false, 127, 0, -1, -1, 0 }, + [564] = { "fre064", false, 127, 0, -1, -1, 0 }, + [565] = { "fre065", false, 127, 0, -1, -1, 0 }, + [566] = { "fre066", false, 127, 0, -1, -1, 0 }, + [567] = { "fre067", false, 127, 0, -1, -1, 0 }, + [568] = { "fre068", false, 127, 0, -1, -1, 0 }, + [569] = { "fre069", false, 127, 0, -1, -1, 0 }, + [570] = { "fre070", false, 127, 0, -1, -1, 0 }, + [571] = { "fre071", false, 127, 0, -1, -1, 0 }, + [572] = { "fre072", false, 127, 0, -1, -1, 0 }, + [573] = { "fre073", false, 127, 0, -1, -1, 0 }, + [574] = { "fre074", false, 127, 0, -1, -1, 0 }, + [575] = { "fre075", false, 127, 0, -1, -1, 0 }, + [576] = { "fre076", false, 127, 0, -1, -1, 0 }, + [577] = { "fre077", false, 127, 0, -1, -1, 0 }, + [578] = { "fre078", false, 127, 0, -1, -1, 0 }, + [579] = { "fre079", false, 127, 0, -1, -1, 0 }, + [580] = { "fre080", false, 127, 0, -1, -1, 0 }, + [581] = { "fre081", false, 127, 0, -1, -1, 0 }, + [582] = { "fre082", false, 127, 0, -1, -1, 0 }, + [583] = { "fre083", false, 127, 0, -1, -1, 0 }, + [584] = { "fre084", false, 127, 0, -1, -1, 0 }, + [585] = { "fre085", false, 127, 0, -1, -1, 0 }, + [586] = { "fre086", false, 127, 0, -1, -1, 0 }, + [587] = { "fre087", false, 127, 0, -1, -1, 0 }, + [588] = { "fre088", false, 127, 0, -1, -1, 0 }, + [589] = { "fre089", false, 127, 0, -1, -1, 0 }, + [590] = { "fre090", false, 127, 0, -1, -1, 0 }, + [591] = { "fre091", false, 127, 0, -1, -1, 0 }, + [592] = { "fre092", false, 127, 0, -1, -1, 0 }, + [593] = { "fre093", false, 127, 0, -1, -1, 0 }, + [594] = { "fre094", false, 127, 0, -1, -1, 0 }, + [595] = { "fre095", false, 127, 0, -1, -1, 0 }, + [596] = { "fre096", false, 127, 0, -1, -1, 0 }, + [597] = { "fre097", false, 127, 0, -1, -1, 0 }, + [598] = { "fre098", false, 127, 0, -1, -1, 0 }, + [599] = { "fre099", false, 127, 0, -1, -1, 0 }, + [600] = { "fre100", false, 127, 0, -1, -1, 0 }, + [601] = { "fre101", false, 127, 0, -1, -1, 0 }, + [602] = { "fre102", false, 127, 0, -1, -1, 0 }, + [603] = { "fre103", false, 127, 0, -1, -1, 0 }, + [604] = { "fre104", false, 127, 0, -1, -1, 0 }, + [605] = { "fre105", false, 127, 0, -1, -1, 0 }, + [606] = { "fre106", false, 127, 0, -1, -1, 0 }, + [607] = { "fre107", false, 127, 0, -1, -1, 0 }, + [608] = { "fre108", false, 127, 0, -1, -1, 0 }, + [609] = { "fre109", false, 127, 0, -1, -1, 0 }, + [610] = { "fre110", false, 127, 0, -1, -1, 0 }, + [611] = { "fre111", false, 127, 0, -1, -1, 0 }, + [612] = { "fre112", false, 127, 0, -1, -1, 0 }, + [613] = { "fre113", false, 127, 0, -1, -1, 0 }, + [614] = { "fre114", false, 127, 0, -1, -1, 0 }, + [615] = { "fre115", false, 127, 0, -1, -1, 0 }, + [616] = { "fre116", false, 127, 0, -1, -1, 0 }, + [617] = { "fre117", false, 127, 0, -1, -1, 0 }, + [618] = { "fre118", false, 127, 0, -1, -1, 0 }, + [619] = { "fre119", false, 127, 0, -1, -1, 0 }, + [620] = { "fre120", false, 127, 0, -1, -1, 0 }, + [621] = { "fre121", false, 127, 0, -1, -1, 0 }, + [622] = { "fre122", false, 127, 0, -1, -1, 0 }, + [623] = { "fre123", false, 127, 0, -1, -1, 0 }, + [624] = { "fre124", false, 127, 0, -1, -1, 0 }, + [625] = { "fre125", false, 127, 0, -1, -1, 0 }, + [626] = { "fre126", false, 127, 0, -1, -1, 0 }, + [627] = { "fre127", false, 127, 0, -1, -1, 0 }, + [628] = { "fre128", false, 127, 0, -1, -1, 0 }, + [629] = { "fre129", false, 127, 0, -1, -1, 0 }, + [630] = { "fre130", false, 127, 0, -1, -1, 0 }, + [631] = { "fre131", false, 127, 0, -1, -1, 0 }, + [632] = { "fre132", false, 127, 0, -1, -1, 0 }, + [633] = { "fre133", false, 127, 0, -1, -1, 0 }, + [634] = { "fre134", false, 127, 0, -1, -1, 0 }, + [635] = { "fre135", false, 127, 0, -1, -1, 0 }, + [636] = { "fre136", false, 127, 0, -1, -1, 0 }, + [637] = { "fre137", false, 127, 0, -1, -1, 0 }, + [638] = { "fre138", false, 127, 0, -1, -1, 0 }, + [639] = { "fre139", false, 127, 0, -1, -1, 0 }, + [640] = { "fre140", false, 127, 0, -1, -1, 0 }, + [641] = { "fre141", false, 127, 0, -1, -1, 0 }, + [642] = { "fre142", false, 127, 0, -1, -1, 0 }, + [643] = { "fre143", false, 127, 0, -1, -1, 0 }, + [644] = { "fre144", false, 127, 0, -1, -1, 0 }, + [645] = { "fre145", false, 127, 0, -1, -1, 0 }, + [646] = { "fre146", false, 127, 0, -1, -1, 0 }, + [647] = { "fre147", false, 127, 0, -1, -1, 0 }, + [648] = { "fre148", false, 127, 0, -1, -1, 0 }, + [649] = { "fre149", false, 127, 0, -1, -1, 0 }, + [650] = { "fre150", false, 127, 0, -1, -1, 0 }, + [651] = { "fre151", false, 127, 0, -1, -1, 0 }, + [652] = { "fre152", false, 127, 0, -1, -1, 0 }, + [653] = { "fre153", false, 127, 0, -1, -1, 0 }, + [654] = { "fre154", false, 127, 0, -1, -1, 0 }, + [655] = { "fre155", false, 127, 0, -1, -1, 0 }, + [656] = { "fre156", false, 127, 0, -1, -1, 0 }, + [657] = { "fre157", false, 127, 0, -1, -1, 0 }, + [658] = { "fre158", false, 127, 0, -1, -1, 0 }, + [659] = { "fre159", false, 127, 0, -1, -1, 0 }, + [660] = { "fre160", false, 127, 0, -1, -1, 0 }, + [661] = { "fre161", false, 127, 0, -1, -1, 0 }, + [662] = { "fre162", false, 127, 0, -1, -1, 0 }, + [663] = { "fre163", false, 127, 0, -1, -1, 0 }, + [664] = { "fre164", false, 127, 0, -1, -1, 0 }, + [665] = { "fre165", false, 127, 0, -1, -1, 0 }, + [666] = { "fre166", false, 127, 0, -1, -1, 0 }, + [667] = { "fre167", false, 127, 0, -1, -1, 0 }, + [668] = { "fre168", false, 127, 0, -1, -1, 0 }, + [669] = { "fre169", false, 127, 0, -1, -1, 0 }, + [670] = { "fre170", false, 127, 0, -1, -1, 0 }, + [671] = { "fre171", false, 127, 0, -1, -1, 0 }, + [672] = { "fre172", false, 127, 0, -1, -1, 0 }, + [673] = { "fre173", false, 127, 0, -1, -1, 0 }, + [674] = { "fre174", false, 127, 0, -1, -1, 0 }, + [675] = { "fre175", false, 127, 0, -1, -1, 0 }, + [676] = { "fre176", false, 127, 0, -1, -1, 0 }, + [677] = { "fre177", false, 127, 0, -1, -1, 0 }, + [678] = { "fre178", false, 127, 0, -1, -1, 0 }, + [679] = { "fre179", false, 127, 0, -1, -1, 0 }, + [680] = { "fre180", false, 127, 0, -1, -1, 0 }, + [681] = { "fre181", false, 127, 0, -1, -1, 0 }, + [682] = { "fre182", false, 127, 0, -1, -1, 0 }, + [683] = { "fre183", false, 127, 0, -1, -1, 0 }, + [684] = { "fre184", false, 127, 0, -1, -1, 0 }, + [685] = { "fre185", false, 127, 0, -1, -1, 0 }, + [686] = { "fre186", false, 127, 0, -1, -1, 0 }, + [687] = { "fre187", false, 127, 0, -1, -1, 0 }, + [688] = { "fre188", false, 127, 0, -1, -1, 0 }, + [689] = { "fre189", false, 127, 0, -1, -1, 0 }, + [690] = { "fre190", false, 127, 0, -1, -1, 0 }, + [691] = { "fre191", false, 127, 0, -1, -1, 0 }, + [692] = { "fre192", false, 127, 0, -1, -1, 0 }, + [693] = { "fre193", false, 127, 0, -1, -1, 0 }, + [694] = { "fre194", false, 127, 0, -1, -1, 0 }, + [695] = { "fre195", false, 127, 0, -1, -1, 0 }, + [696] = { "fre196", false, 127, 0, -1, -1, 0 }, + [697] = { "fre197", false, 127, 0, -1, -1, 0 }, + [698] = { "fre198", false, 127, 0, -1, -1, 0 }, + [699] = { "fre199", false, 127, 0, -1, -1, 0 }, +}; diff --git a/src/sounds.h b/src/sounds.h new file mode 100644 index 0000000..4d9ab62 --- /dev/null +++ b/src/sounds.h @@ -0,0 +1,508 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Created by the sound utility written by Dave Taylor. + * Kept as a sample, DOOM2 sounds. Frozen. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __SOUNDS__ +#define __SOUNDS__ + +// +// SoundFX struct. +// + +struct sfxinfo_struct; + +typedef struct sfxinfo_struct sfxinfo_t; + +struct sfxinfo_struct { + + // up to 6-character name + const char *name; // CPhipps - const + + // Sfx singularity (only one at a time) + int singularity; + + // Sfx priority + int priority; + + // referenced sound if a link + sfxinfo_t *link; + + // pitch if a link + int pitch; + + // volume if a link + int volume; + + // sound data + void *data; + + // this field was related to caching (now meaningless) + int usefulness; + + // lump number of sfx + int lumpnum; +}; + +// +// MusicInfo struct. +// + +typedef struct { + // up to 6-character name + const char *name; // CPhipps - const + + // lump number of music + int lumpnum; + + /* music data - cphipps 4/11 made const void* */ + const void *data; + + // music handle once registered + int handle; +} musicinfo_t; + +// the complete set of sound effects +extern sfxinfo_t S_sfx[]; + +// the complete set of music +extern musicinfo_t S_music[]; + +// +// Identifiers for all music in game. +// + +typedef enum { + mus_None, + mus_e1m1, + mus_e1m2, + mus_e1m3, + mus_e1m4, + mus_e1m5, + mus_e1m6, + mus_e1m7, + mus_e1m8, + mus_e1m9, + mus_e2m1, + mus_e2m2, + mus_e2m3, + mus_e2m4, + mus_e2m5, + mus_e2m6, + mus_e2m7, + mus_e2m8, + mus_e2m9, + mus_e3m1, + mus_e3m2, + mus_e3m3, + mus_e3m4, + mus_e3m5, + mus_e3m6, + mus_e3m7, + mus_e3m8, + mus_e3m9, + mus_inter, + mus_intro, + mus_bunny, + mus_victor, + mus_introa, + mus_runnin, + mus_stalks, + mus_countd, + mus_betwee, + mus_doom, + mus_the_da, + mus_shawn, + mus_ddtblu, + mus_in_cit, + mus_dead, + mus_stlks2, + mus_theda2, + mus_doom2, + mus_ddtbl2, + mus_runni2, + mus_dead2, + mus_stlks3, + mus_romero, + mus_shawn2, + mus_messag, + mus_count2, + mus_ddtbl3, + mus_ampie, + mus_theda3, + mus_adrian, + mus_messg2, + mus_romer2, + mus_tense, + mus_shawn3, + mus_openin, + mus_evil, + mus_ultima, + mus_read_m, + mus_dm2ttl, + mus_dm2int, + NUMMUSIC +} musicenum_t; + +// +// Identifiers for all sfx in game. +// + +typedef enum { + sfx_None, + sfx_pistol, + sfx_shotgn, + sfx_sgcock, + sfx_dshtgn, + sfx_dbopn, + sfx_dbcls, + sfx_dbload, + sfx_plasma, + sfx_bfg, + sfx_sawup, + sfx_sawidl, + sfx_sawful, + sfx_sawhit, + sfx_rlaunc, + sfx_rxplod, + sfx_firsht, + sfx_firxpl, + sfx_pstart, + sfx_pstop, + sfx_doropn, + sfx_dorcls, + sfx_stnmov, + sfx_swtchn, + sfx_swtchx, + sfx_plpain, + sfx_dmpain, + sfx_popain, + sfx_vipain, + sfx_mnpain, + sfx_pepain, + sfx_slop, + sfx_itemup, + sfx_wpnup, + sfx_oof, + sfx_telept, + sfx_posit1, + sfx_posit2, + sfx_posit3, + sfx_bgsit1, + sfx_bgsit2, + sfx_sgtsit, + sfx_cacsit, + sfx_brssit, + sfx_cybsit, + sfx_spisit, + sfx_bspsit, + sfx_kntsit, + sfx_vilsit, + sfx_mansit, + sfx_pesit, + sfx_sklatk, + sfx_sgtatk, + sfx_skepch, + sfx_vilatk, + sfx_claw, + sfx_skeswg, + sfx_pldeth, + sfx_pdiehi, + sfx_podth1, + sfx_podth2, + sfx_podth3, + sfx_bgdth1, + sfx_bgdth2, + sfx_sgtdth, + sfx_cacdth, + sfx_skldth, + sfx_brsdth, + sfx_cybdth, + sfx_spidth, + sfx_bspdth, + sfx_vildth, + sfx_kntdth, + sfx_pedth, + sfx_skedth, + sfx_posact, + sfx_bgact, + sfx_dmact, + sfx_bspact, + sfx_bspwlk, + sfx_vilact, + sfx_noway, + sfx_barexp, + sfx_punch, + sfx_hoof, + sfx_metal, + sfx_chgun, + sfx_tink, + sfx_bdopn, + sfx_bdcls, + sfx_itmbk, + sfx_flame, + sfx_flamst, + sfx_getpow, + sfx_bospit, + sfx_boscub, + sfx_bossit, + sfx_bospn, + sfx_bosdth, + sfx_manatk, + sfx_mandth, + sfx_sssit, + sfx_ssdth, + sfx_keenpn, + sfx_keendt, + sfx_skeact, + sfx_skesit, + sfx_skeatk, + sfx_radio, + + /* killough 11/98: dog sounds */ + sfx_dgsit, + sfx_dgatk, + sfx_dgact, + sfx_dgdth, + sfx_dgpain, + + //e6y + sfx_secret, + sfx_gibdth, + // Everything from here to 500 is reserved + + /* Free sound effect slots for DEHEXTRA. Offset agreed upon with Eternity devs. -SH */ + sfx_fre000 = 500, + sfx_fre001, + sfx_fre002, + sfx_fre003, + sfx_fre004, + sfx_fre005, + sfx_fre006, + sfx_fre007, + sfx_fre008, + sfx_fre009, + sfx_fre010, + sfx_fre011, + sfx_fre012, + sfx_fre013, + sfx_fre014, + sfx_fre015, + sfx_fre016, + sfx_fre017, + sfx_fre018, + sfx_fre019, + sfx_fre020, + sfx_fre021, + sfx_fre022, + sfx_fre023, + sfx_fre024, + sfx_fre025, + sfx_fre026, + sfx_fre027, + sfx_fre028, + sfx_fre029, + sfx_fre030, + sfx_fre031, + sfx_fre032, + sfx_fre033, + sfx_fre034, + sfx_fre035, + sfx_fre036, + sfx_fre037, + sfx_fre038, + sfx_fre039, + sfx_fre040, + sfx_fre041, + sfx_fre042, + sfx_fre043, + sfx_fre044, + sfx_fre045, + sfx_fre046, + sfx_fre047, + sfx_fre048, + sfx_fre049, + sfx_fre050, + sfx_fre051, + sfx_fre052, + sfx_fre053, + sfx_fre054, + sfx_fre055, + sfx_fre056, + sfx_fre057, + sfx_fre058, + sfx_fre059, + sfx_fre060, + sfx_fre061, + sfx_fre062, + sfx_fre063, + sfx_fre064, + sfx_fre065, + sfx_fre066, + sfx_fre067, + sfx_fre068, + sfx_fre069, + sfx_fre070, + sfx_fre071, + sfx_fre072, + sfx_fre073, + sfx_fre074, + sfx_fre075, + sfx_fre076, + sfx_fre077, + sfx_fre078, + sfx_fre079, + sfx_fre080, + sfx_fre081, + sfx_fre082, + sfx_fre083, + sfx_fre084, + sfx_fre085, + sfx_fre086, + sfx_fre087, + sfx_fre088, + sfx_fre089, + sfx_fre090, + sfx_fre091, + sfx_fre092, + sfx_fre093, + sfx_fre094, + sfx_fre095, + sfx_fre096, + sfx_fre097, + sfx_fre098, + sfx_fre099, + sfx_fre100, + sfx_fre101, + sfx_fre102, + sfx_fre103, + sfx_fre104, + sfx_fre105, + sfx_fre106, + sfx_fre107, + sfx_fre108, + sfx_fre109, + sfx_fre110, + sfx_fre111, + sfx_fre112, + sfx_fre113, + sfx_fre114, + sfx_fre115, + sfx_fre116, + sfx_fre117, + sfx_fre118, + sfx_fre119, + sfx_fre120, + sfx_fre121, + sfx_fre122, + sfx_fre123, + sfx_fre124, + sfx_fre125, + sfx_fre126, + sfx_fre127, + sfx_fre128, + sfx_fre129, + sfx_fre130, + sfx_fre131, + sfx_fre132, + sfx_fre133, + sfx_fre134, + sfx_fre135, + sfx_fre136, + sfx_fre137, + sfx_fre138, + sfx_fre139, + sfx_fre140, + sfx_fre141, + sfx_fre142, + sfx_fre143, + sfx_fre144, + sfx_fre145, + sfx_fre146, + sfx_fre147, + sfx_fre148, + sfx_fre149, + sfx_fre150, + sfx_fre151, + sfx_fre152, + sfx_fre153, + sfx_fre154, + sfx_fre155, + sfx_fre156, + sfx_fre157, + sfx_fre158, + sfx_fre159, + sfx_fre160, + sfx_fre161, + sfx_fre162, + sfx_fre163, + sfx_fre164, + sfx_fre165, + sfx_fre166, + sfx_fre167, + sfx_fre168, + sfx_fre169, + sfx_fre170, + sfx_fre171, + sfx_fre172, + sfx_fre173, + sfx_fre174, + sfx_fre175, + sfx_fre176, + sfx_fre177, + sfx_fre178, + sfx_fre179, + sfx_fre180, + sfx_fre181, + sfx_fre182, + sfx_fre183, + sfx_fre184, + sfx_fre185, + sfx_fre186, + sfx_fre187, + sfx_fre188, + sfx_fre189, + sfx_fre190, + sfx_fre191, + sfx_fre192, + sfx_fre193, + sfx_fre194, + sfx_fre195, + sfx_fre196, + sfx_fre197, + sfx_fre198, + sfx_fre199, + + NUMSFX +} sfxenum_t; + +#endif diff --git a/src/st_lib.c b/src/st_lib.c new file mode 100644 index 0000000..9e5a2c7 --- /dev/null +++ b/src/st_lib.c @@ -0,0 +1,375 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * The status bar widget code. + * + *-----------------------------------------------------------------------------*/ + +#include "doomdef.h" +#include "doomstat.h" +#include "v_video.h" +#include "w_wad.h" +#include "st_stuff.h" +#include "st_lib.h" +#include "r_main.h" +#include "lprintf.h" + +int sts_always_red; //jff 2/18/98 control to disable status color changes +int sts_pct_always_gray; // killough 2/21/98: always gray %'s? bug or feature? +int sts_armorcolor_type; // control to enable armor color depending on type + +// +// STlib_init() +// +void STlib_init(void) +{ + // cph - no longer hold STMINUS pointer +} + +// +// STlib_initNum() +// +// Initializes an st_number_t widget +// +// Passed the widget, its position, the patches for the digits, a pointer +// to the value displayed, a pointer to the on/off control, and the width +// Returns nothing +// +void STlib_initNum +( st_number_t* n, + int x, + int y, + const patchnum_t* pl, + int* num, + dboolean* on, + int width ) +{ + n->x = x; + n->y = y; + n->oldnum = 0; + n->width = width; + n->num = num; + n->on = on; + n->p = pl; +} + +/* + * STlib_drawNum() + * + * A fairly efficient way to draw a number based on differences from the + * old number. + * + * Passed a st_number_t widget, a color range for output, and a flag + * indicating whether refresh is needed. + * Returns nothing + * + * jff 2/16/98 add color translation to digit output + * cphipps 10/99 - const pointer to colour trans table, made function static + */ +static void STlib_drawNum +( st_number_t* n, + int cm, + dboolean refresh ) +{ + + int numdigits = n->width; + int num = *n->num; + + int w = n->p[0].width; + int h = n->p[0].height; + int x = n->x; + + int neg; + + // leban 1/20/99: + // strange that somebody went through all the work to draw only the + // differences, and then went and constantly redrew all the numbers. + // return without drawing if the number didn't change and the bar + // isn't refreshing. + if(n->oldnum == num && !refresh) + return; + + // CPhipps - compact some code, use num instead of *n->num + if ((neg = (n->oldnum = num) < 0)) + { + 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; + +#ifdef RANGECHECK + if (n->y - ST_Y < 0) + I_Error("STlib_drawNum: n->y - ST_Y < 0"); +#endif + + V_CopyRect(BG, FG, x, n->y, w * numdigits, h, VPT_STRETCH | VPT_ALIGN_BOTTOM); + + // if non-number, do not draw it + if (num == 1994) + return; + + x = n->x; + + //jff 2/16/98 add color translation to digit output + // in the special case of 0, you draw 0 + if (!num) + // CPhipps - patch drawing updated, reformatted + V_DrawNumPatch(x - w, n->y, FG, n->p[0].lumpnum, cm, + (((cm!=CR_DEFAULT) && !sts_always_red) ? VPT_TRANS : VPT_NONE) | VPT_ALIGN_BOTTOM); + + // draw the new number + //jff 2/16/98 add color translation to digit output + while (num && numdigits--) { + // CPhipps - patch drawing updated, reformatted + x -= w; + V_DrawNumPatch(x, n->y, FG, n->p[num % 10].lumpnum, cm, + (((cm!=CR_DEFAULT) && !sts_always_red) ? VPT_TRANS : VPT_NONE) | VPT_ALIGN_BOTTOM); + num /= 10; + } + + // draw a minus sign if necessary + //jff 2/16/98 add color translation to digit output + // cph - patch drawing updated, load by name instead of acquiring pointer earlier + if (neg) + V_DrawNamePatch(x - w, n->y, FG, "STTMINUS", cm, + (((cm!=CR_DEFAULT) && !sts_always_red) ? VPT_TRANS : VPT_NONE) | VPT_ALIGN_BOTTOM); +} + +/* + * STlib_updateNum() + * + * Draws a number conditionally based on the widget's enable + * + * Passed a number widget, the output color range, and a refresh flag + * Returns nothing + * + * jff 2/16/98 add color translation to digit output + * cphipps 10/99 - make that pointer const + */ +void STlib_updateNum +( st_number_t* n, + int cm, + dboolean refresh ) +{ + if (*n->on) STlib_drawNum(n, cm, refresh); +} + +// +// STlib_initPercent() +// +// Initialize a st_percent_t number with percent sign widget +// +// Passed a st_percent_t widget, the position, the digit patches, a pointer +// to the number to display, a pointer to the enable flag, and patch +// for the percent sign. +// Returns nothing. +// +void STlib_initPercent +( st_percent_t* p, + int x, + int y, + const patchnum_t* pl, + int* num, + dboolean* on, + const patchnum_t* percent ) +{ + STlib_initNum(&p->n, x, y, pl, num, on, 3); + p->p = percent; +} + +/* + * STlib_updatePercent() + * + * Draws a number/percent conditionally based on the widget's enable + * + * Passed a precent widget, the output color range, and a refresh flag + * Returns nothing + * + * jff 2/16/98 add color translation to digit output + * cphipps - const for pointer to the colour translation table + */ + +void STlib_updatePercent +( st_percent_t* per, + int cm, + int refresh ) +{ + if (*per->n.on && (refresh || (per->n.oldnum != *per->n.num))) { + // killough 2/21/98: fix percents not updated; + /* CPhipps - make %'s only be updated if number changed */ + // CPhipps - patch drawing updated + V_DrawNumPatch(per->n.x, per->n.y, FG, per->p->lumpnum, + sts_pct_always_gray ? CR_GRAY : cm, + (sts_always_red ? VPT_NONE : VPT_TRANS) | VPT_ALIGN_BOTTOM); + } + + STlib_updateNum(&per->n, cm, refresh); +} + +// +// STlib_initMultIcon() +// +// Initialize a st_multicon_t widget, used for a multigraphic display +// like the status bar's keys. +// +// Passed a st_multicon_t widget, the position, the graphic patches, a pointer +// to the numbers representing what to display, and pointer to the enable flag +// Returns nothing. +// +void STlib_initMultIcon +( st_multicon_t* i, + int x, + int y, + const patchnum_t* il, + int* inum, + dboolean* on ) +{ + i->x = x; + i->y = y; + i->oldinum = -1; + i->inum = inum; + i->on = on; + i->p = il; +} + +// +// STlib_updateMultIcon() +// +// Draw a st_multicon_t widget, used for a multigraphic display +// like the status bar's keys. Displays each when the control +// numbers change or refresh is true +// +// Passed a st_multicon_t widget, and a refresh flag +// Returns nothing. +// +void STlib_updateMultIcon +( st_multicon_t* mi, + dboolean refresh ) +{ + int w; + int h; + int x; + int y; + + if (*mi->on && (mi->oldinum != *mi->inum || refresh)) + { + if (mi->oldinum != -1) + { + x = mi->x - mi->p[mi->oldinum].leftoffset; + y = mi->y - mi->p[mi->oldinum].topoffset; + w = mi->p[mi->oldinum].width; + h = mi->p[mi->oldinum].height; + +#ifdef RANGECHECK + if (y - ST_Y < 0) + I_Error("STlib_updateMultIcon: y - ST_Y < 0"); +#endif + + V_CopyRect(BG, FG, x, y, w, h, VPT_STRETCH | VPT_ALIGN_BOTTOM); + } + if (*mi->inum != -1) // killough 2/16/98: redraw only if != -1 + V_DrawNumPatch(mi->x, mi->y, FG, mi->p[*mi->inum].lumpnum, CR_DEFAULT, VPT_ALIGN_BOTTOM); + mi->oldinum = *mi->inum; + } +} + +// +// STlib_initBinIcon() +// +// Initialize a st_binicon_t widget, used for a multinumber display +// like the status bar's weapons, that are present or not. +// +// Passed a st_binicon_t widget, the position, the digit patches, a pointer +// to the flags representing what is displayed, and pointer to the enable flag +// Returns nothing. +// +void STlib_initBinIcon +( st_binicon_t* b, + int x, + int y, + const patchnum_t* i, + dboolean* val, + dboolean* on ) +{ + b->x = x; + b->y = y; + b->oldval = 0; + b->val = val; + b->on = on; + b->p = i; +} + +// +// STlib_updateBinIcon() +// +// DInitialize a st_binicon_t widget, used for a multinumber display +// like the status bar's weapons, that are present or not. +// +// Draw a st_binicon_t widget, used for a multinumber display +// like the status bar's weapons that are present or not. Displays each +// when the control flag changes or refresh is true +// +// Passed a st_binicon_t widget, and a refresh flag +// Returns nothing. +// +void STlib_updateBinIcon +( st_binicon_t* bi, + dboolean refresh ) +{ + int x; + int y; + int w; + int h; + + if (*bi->on && (bi->oldval != *bi->val || refresh)) + { + x = bi->x - bi->p->leftoffset; + y = bi->y - bi->p->topoffset; + w = bi->p->width; + h = bi->p->height; + +#ifdef RANGECHECK + if (y - ST_Y < 0) + I_Error("STlib_updateBinIcon: y - ST_Y < 0"); +#endif + + if (*bi->val) + V_DrawNumPatch(bi->x, bi->y, FG, bi->p->lumpnum, CR_DEFAULT, VPT_STRETCH); + else + V_CopyRect(BG, FG, x, y, w, h, VPT_STRETCH); + + bi->oldval = *bi->val; + } +} diff --git a/src/st_lib.h b/src/st_lib.h new file mode 100644 index 0000000..8c6dfaa --- /dev/null +++ b/src/st_lib.h @@ -0,0 +1,209 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * The status bar widget definitions and prototypes + * + *-----------------------------------------------------------------------------*/ + +#ifndef __STLIB__ +#define __STLIB__ + +// We are referring to patches. +#include "r_defs.h" +#include "v_video.h" // color ranges + +// +// Background and foreground screen numbers +// +#define BG 4 +#define FG 0 + +// +// Typedefs of widgets +// + +// Number widget + +typedef struct +{ + // upper right-hand corner + // of the number (right-justified) + int x; + int y; + + // max # of digits in number + int width; + + // last number value + int oldnum; + + // pointer to current value + int* num; + + // pointer to dboolean stating + // whether to update number + dboolean* on; + + // list of patches for 0-9 + const patchnum_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 + const patchnum_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 dboolean stating + // whether to update icon + dboolean* on; + + // list of icons + const patchnum_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 + dboolean oldval; + + // pointer to current icon status + dboolean* val; + + // pointer to dboolean + // stating whether to update icon + dboolean* on; + + const patchnum_t* p; // icon + int data; // user data +} st_binicon_t; + +// +// Widget creation, access, and update routines +// + +// Initializes widget library. +// More precisely, initialize STMINUS, +// everything else is done somewhere else. +// +void STlib_init(void); + +// Number widget routines +void STlib_initNum +( st_number_t* n, + int x, + int y, + const patchnum_t* pl, + int* num, + dboolean* on, + int width ); + +void STlib_updateNum +( st_number_t* n, + int cm, + dboolean refresh ); + + +// Percent widget routines +void STlib_initPercent +( st_percent_t* p, + int x, + int y, + const patchnum_t* pl, + int* num, + dboolean* on, + const patchnum_t* percent ); + + +void STlib_updatePercent +( st_percent_t* per, + int cm, + int refresh ); + + +// Multiple Icon widget routines +void STlib_initMultIcon +( st_multicon_t* mi, + int x, + int y, + const patchnum_t* il, + int* inum, + dboolean* on ); + + +void STlib_updateMultIcon +( st_multicon_t* mi, + dboolean refresh ); + +// Binary Icon widget routines + +void STlib_initBinIcon +( st_binicon_t* b, + int x, + int y, + const patchnum_t* i, + dboolean* val, + dboolean* on ); + +void STlib_updateBinIcon +( st_binicon_t* bi, + dboolean refresh ); + +#endif diff --git a/src/st_stuff.c b/src/st_stuff.c new file mode 100644 index 0000000..56bced7 --- /dev/null +++ b/src/st_stuff.c @@ -0,0 +1,1273 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Status bar code. + * Does the face/direction indicator animatin. + * Does palette indicators as well (red pain/berserk, bright pickup) + * + *-----------------------------------------------------------------------------*/ + +#include "doomdef.h" +#include "doomstat.h" +#include "m_random.h" +#include "i_video.h" +#include "w_wad.h" +#include "st_stuff.h" +#include "st_lib.h" +#include "r_main.h" +#include "am_map.h" +#include "m_cheat.h" +#include "s_sound.h" +#include "sounds.h" +#include "dstrings.h" +#include "r_draw.h" +#include "e6y.h"//e6y + +// +// STATUS BAR DATA +// + +int ST_SCALED_HEIGHT; +int ST_SCALED_WIDTH; +int ST_SCALED_Y; +int ST_SCALED_OFFSETX; + +// Palette indices. +// For damage/bonus red-/gold-shifts +#define STARTREDPALS 1 +#define STARTBONUSPALS 9 +#define NUMREDPALS 8 +#define NUMBONUSPALS 4 +// Radiation suit, green shift. +#define RADIATIONPAL 13 + +// Location of status bar +#define ST_X 0 +#define ST_X2 104 + +// proff 08/18/98: Changed for high-res +#define ST_FX (ST_X+143) +#define ST_FY (ST_Y+1) +//#define ST_FX 143 +//#define ST_FY 169 + +// Should be set to patch width +// for tall numbers later on +#define ST_TALLNUMWIDTH (tallnum[0]->width) + +// Number of status faces. +#define ST_NUMPAINFACES 5 +#define ST_NUMSTRAIGHTFACES 3 +#define ST_NUMTURNFACES 2 +#define ST_NUMSPECIALFACES 3 + +#define ST_FACESTRIDE \ + (ST_NUMSTRAIGHTFACES+ST_NUMTURNFACES+ST_NUMSPECIALFACES) + +#define ST_NUMEXTRAFACES 2 + +#define ST_NUMFACES \ + (ST_FACESTRIDE*ST_NUMPAINFACES+ST_NUMEXTRAFACES) + +#define ST_TURNOFFSET (ST_NUMSTRAIGHTFACES) +#define ST_OUCHOFFSET (ST_TURNOFFSET + ST_NUMTURNFACES) +#define ST_EVILGRINOFFSET (ST_OUCHOFFSET + 1) +#define ST_RAMPAGEOFFSET (ST_EVILGRINOFFSET + 1) +#define ST_GODFACE (ST_NUMPAINFACES*ST_FACESTRIDE) +#define ST_DEADFACE (ST_GODFACE+1) + +// proff 08/18/98: Changed for high-res +#define ST_FACESX (ST_X+143) +#define ST_FACESY (ST_Y) +//#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? +// I dunno, why don't you go and find out!!! killough + +// AMMO number pos. +#define ST_AMMOWIDTH 3 +// proff 08/18/98: Changed for high-res +#define ST_AMMOX (ST_X+44) +#define ST_AMMOY (ST_Y+3) +//#define ST_AMMOX 44 +//#define ST_AMMOY 171 + +// HEALTH number pos. +#define ST_HEALTHWIDTH 3 +// proff 08/18/98: Changed for high-res +#define ST_HEALTHX (ST_X+90) +#define ST_HEALTHY (ST_Y+3) +//#define ST_HEALTHX 90 +//#define ST_HEALTHY 171 + +// Weapon pos. +// proff 08/18/98: Changed for high-res +#define ST_ARMSX (ST_X+111) +#define ST_ARMSY (ST_Y+4) +#define ST_ARMSBGX (ST_X+104) +#define ST_ARMSBGY (ST_Y) +//#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. +// proff 08/18/98: Changed for high-res +#define ST_FRAGSX (ST_X+138) +#define ST_FRAGSY (ST_Y+3) +//#define ST_FRAGSX 138 +//#define ST_FRAGSY 171 +#define ST_FRAGSWIDTH 2 + +// ARMOR number pos. +#define ST_ARMORWIDTH 3 +// proff 08/18/98: Changed for high-res +#define ST_ARMORX (ST_X+221) +#define ST_ARMORY (ST_Y+3) +//#define ST_ARMORX 221 +//#define ST_ARMORY 171 + +// Key icon positions. +#define ST_KEY0WIDTH 8 +#define ST_KEY0HEIGHT 5 +// proff 08/18/98: Changed for high-res +#define ST_KEY0X (ST_X+239) +#define ST_KEY0Y (ST_Y+3) +//#define ST_KEY0X 239 +//#define ST_KEY0Y 171 +#define ST_KEY1WIDTH ST_KEY0WIDTH +// proff 08/18/98: Changed for high-res +#define ST_KEY1X (ST_X+239) +#define ST_KEY1Y (ST_Y+13) +//#define ST_KEY1X 239 +//#define ST_KEY1Y 181 +#define ST_KEY2WIDTH ST_KEY0WIDTH +// proff 08/18/98: Changed for high-res +#define ST_KEY2X (ST_X+239) +#define ST_KEY2Y (ST_Y+23) +//#define ST_KEY2X 239 +//#define ST_KEY2Y 191 + +// Ammunition counter. +#define ST_AMMO0WIDTH 3 +#define ST_AMMO0HEIGHT 6 +// proff 08/18/98: Changed for high-res +#define ST_AMMO0X (ST_X+288) +#define ST_AMMO0Y (ST_Y+5) +//#define ST_AMMO0X 288 +//#define ST_AMMO0Y 173 +#define ST_AMMO1WIDTH ST_AMMO0WIDTH +// proff 08/18/98: Changed for high-res +#define ST_AMMO1X (ST_X+288) +#define ST_AMMO1Y (ST_Y+11) +//#define ST_AMMO1X 288 +//#define ST_AMMO1Y 179 +#define ST_AMMO2WIDTH ST_AMMO0WIDTH +// proff 08/18/98: Changed for high-res +#define ST_AMMO2X (ST_X+288) +#define ST_AMMO2Y (ST_Y+23) +//#define ST_AMMO2X 288 +//#define ST_AMMO2Y 191 +#define ST_AMMO3WIDTH ST_AMMO0WIDTH +// proff 08/18/98: Changed for high-res +#define ST_AMMO3X (ST_X+288) +#define ST_AMMO3Y (ST_Y+17) +//#define ST_AMMO3X 288 +//#define ST_AMMO3Y 185 + +// Indicate maximum ammunition. +// Only needed because backpack exists. +#define ST_MAXAMMO0WIDTH 3 +#define ST_MAXAMMO0HEIGHT 5 +// proff 08/18/98: Changed for high-res +#define ST_MAXAMMO0X (ST_X+314) +#define ST_MAXAMMO0Y (ST_Y+5) +//#define ST_MAXAMMO0X 314 +//#define ST_MAXAMMO0Y 173 +#define ST_MAXAMMO1WIDTH ST_MAXAMMO0WIDTH +// proff 08/18/98: Changed for high-res +#define ST_MAXAMMO1X (ST_X+314) +#define ST_MAXAMMO1Y (ST_Y+11) +//#define ST_MAXAMMO1X 314 +//#define ST_MAXAMMO1Y 179 +#define ST_MAXAMMO2WIDTH ST_MAXAMMO0WIDTH +// proff 08/18/98: Changed for high-res +#define ST_MAXAMMO2X (ST_X+314) +#define ST_MAXAMMO2Y (ST_Y+23) +//#define ST_MAXAMMO2X 314 +//#define ST_MAXAMMO2Y 191 +#define ST_MAXAMMO3WIDTH ST_MAXAMMO0WIDTH +// proff 08/18/98: Changed for high-res +#define ST_MAXAMMO3X (ST_X+314) +#define ST_MAXAMMO3Y (ST_Y+17) +//#define ST_MAXAMMO3X 314 +//#define ST_MAXAMMO3Y 185 + +// killough 2/8/98: weapon info position macros UNUSED, removed here + +// main player in game +static player_t *plyr; + +// ST_Start() has just been called +static dboolean st_firsttime; + +// used to execute ST_Init() only once +static int veryfirsttime = 1; + +// CPhipps - no longer do direct PLAYPAL handling here + +// used for timing +static unsigned int st_clock; + +// used for making messages go away +static int st_msgcounter=0; + +// used when in chat +static st_chatstateenum_t st_chatstate; + +// whether in automap or first-person +static st_stateenum_t st_gamestate; + +// whether left-side main status bar is active +static dboolean st_statusbaron; + +// whether status bar chat is active +static dboolean st_chat; + +// value of st_chat before message popped up +static dboolean st_oldchat; + +// whether chat window has the cursor on +static dboolean st_cursoron; + +// !deathmatch +static dboolean st_notdeathmatch; + +// !deathmatch && st_statusbaron +static dboolean st_armson; + +// !deathmatch +static dboolean st_fragson; + +// 0-9, tall numbers +static patchnum_t tallnum[10]; + +// tall % sign +static patchnum_t tallpercent; + +// 0-9, short, yellow (,different!) numbers +static patchnum_t shortnum[10]; + +// 3 key-cards, 3 skulls, 3 card/skull combos +// jff 2/24/98 extend number of patches by three skull/card combos +static patchnum_t keys[NUMCARDS+3]; + +// face status patches +static patchnum_t faces[ST_NUMFACES]; + +// face background +static patchnum_t faceback; // CPhipps - single background, translated for different players + +//e6y: status bar background +static patchnum_t stbarbg = {0}; +patchnum_t grnrock; +patchnum_t brdr_t, brdr_b, brdr_l, brdr_r; +patchnum_t brdr_tl, brdr_tr, brdr_bl, brdr_br; + +// main bar right +static patchnum_t armsbg; + +// weapon ownership patches +static patchnum_t arms[6][2]; + +// ready-weapon widget +static st_number_t w_ready; + +//jff 2/16/98 status color change levels +int ammo_red; // ammo percent less than which status is red +int ammo_yellow; // ammo percent less is yellow more green +int health_red; // health amount less than which status is red +int health_yellow; // health amount less than which status is yellow +int health_green; // health amount above is blue, below is green +int armor_red; // armor amount less than which status is red +int armor_yellow; // armor amount less than which status is yellow +int armor_green; // armor amount above is blue, below is green + +ammo_colour_behaviour_t ammo_colour_behaviour; +const char *ammo_colour_behaviour_list[ammo_colour_behaviour_max] = { + "no", + "full ammo only", + "yes" +}; + + // 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 appopriately pained face +static int st_oldhealth = -1; + +// used for evil grin +static dboolean 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; + +extern char *mapnames[]; + +// +// STATUS BAR CODE +// + +static void ST_Stop(void); + +// [FG] support widescreen status bar backgrounds + +void ST_SetScaledWidth(void) +{ + int width = stbarbg.width; + + if (width == 0) + width = ST_WIDTH; + + switch (render_stretch_hud) + { + case patch_stretch_16x10: + ST_SCALED_WIDTH = width * patches_scalex; + break; + case patch_stretch_4x3: + ST_SCALED_WIDTH = width * WIDE_SCREENWIDTH / 320; + break; + case patch_stretch_full: + ST_SCALED_WIDTH = width * SCREENWIDTH / 320; + break; + } + + ST_SCALED_WIDTH = (ST_SCALED_WIDTH + 3) & (int)~3; + + if (ST_SCALED_WIDTH > SCREENWIDTH) + ST_SCALED_WIDTH = SCREENWIDTH; + + ST_SCALED_OFFSETX = (SCREENWIDTH - ST_SCALED_WIDTH) / 2; +} + +static void ST_refreshBackground(void) +{ + int y = ST_Y; + enum patch_translation_e flags = VPT_ALIGN_LEFT_TOP; + + if (st_statusbaron) + { + flags = VPT_ALIGN_BOTTOM; + + // Applies palette to backfill + if (V_GetMode() == VID_MODE15 || V_GetMode() == VID_MODE16 || V_GetMode() == VID_MODE32) + R_FillBackScreen(); + + V_DrawNumPatch(ST_X, y, BG, stbarbg.lumpnum, CR_DEFAULT, flags); + if (!deathmatch) + { + V_DrawNumPatch(ST_ARMSBGX, y, BG, armsbg.lumpnum, CR_DEFAULT, flags); + } + + // killough 3/7/98: make face background change with displayplayer + if (netgame) + { + V_DrawNumPatch(ST_FX, y, BG, faceback.lumpnum, + displayplayer ? CR_LIMIT+displayplayer : CR_DEFAULT, + displayplayer ? (VPT_TRANS | VPT_ALIGN_BOTTOM) : flags); + } + V_CopyRect(BG, FG, ST_X + ST_SCALED_OFFSETX, SCREENHEIGHT - ST_SCALED_HEIGHT, ST_SCALED_WIDTH, ST_SCALED_HEIGHT, VPT_NONE); + } +} + + +// Respond to keyboard input events, +// intercept cheats. +dboolean ST_Responder(event_t *ev) +{ + // Filter automap on/off. + if (ev->type == ev_keyup && (ev->data1 & 0xffff0000) == AM_MSGHEADER) + { + switch(ev->data1) + { + case AM_MSGENTERED: + st_gamestate = AutomapState; + st_firsttime = true; + break; + + case AM_MSGEXITED: + st_gamestate = FirstPersonState; + break; + } + } + else // if a user keypress... + if (ev->type == ev_keydown) // Try cheat responder in m_cheat.c + return M_FindCheats(ev->data1); // killough 4/17/98, 5/2/98 + return false; +} + +static int ST_calcPainOffset(void) +{ + static int lastcalc; + static int oldhealth = -1; + int 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_updateFaceWidget(void) +{ + int i; + angle_t badguyangle; + angle_t diffang; + static int lastattackdown = -1; + static int priority = 0; + dboolean 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;iweaponowned[i]) + { + doevilgrin = true; + oldweaponsowned[i] = plyr->weaponowned[i]; + } + } + if (doevilgrin) + { + // evil grin if just picked up weapon + priority = 8; + st_facecount = ST_EVILGRINCOUNT; + st_faceindex = ST_calcPainOffset() + ST_EVILGRINOFFSET; + } + } + + } + + if (priority < 8) + { + if (plyr->damagecount && plyr->attacker && plyr->attacker != plyr->mo) + { + // being attacked + priority = 7; + + // haleyjd 10/12/03: classic DOOM problem of missing OUCH face + // was due to inversion of this test: + // if(plyr->health - st_oldhealth > ST_MUCHPAIN) + // e6y: compatibility optioned + if((default_comp[comp_ouchface]? + (plyr->health - st_oldhealth): + (st_oldhealth - plyr->health)) > ST_MUCHPAIN) + { + // e6y + // There are TWO bugs in the ouch face code. + // Not only was the condition reversed, but the priority system is + // broken in a way that makes the face not work with monster damage. + if(!default_comp[comp_ouchface]) + priority = 8; + + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET; + } + else + { + badguyangle = R_PointToAngle2(plyr->mo->x, + plyr->mo->y, + plyr->attacker->x, + plyr->attacker->y); + + if (badguyangle > plyr->mo->angle) + { + // whether right or left + diffang = badguyangle - plyr->mo->angle; + i = diffang > ANG180; + } + else + { + // whether left or right + diffang = plyr->mo->angle - badguyangle; + i = diffang <= ANG180; + } // confusing, aint it? + + + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset(); + + if (diffang < ANG45) + { + // head-on + st_faceindex += ST_RAMPAGEOFFSET; + } + else if (i) + { + // turn face right + st_faceindex += ST_TURNOFFSET; + } + else + { + // turn face left + st_faceindex += ST_TURNOFFSET+1; + } + } + } + } + + if (priority < 7) + { + // getting hurt because of your own damn stupidity + if (plyr->damagecount) + { + // haleyjd 10/12/03: classic DOOM problem of missing OUCH face + // was due to inversion of this test: + // if(plyr->health - st_oldhealth > ST_MUCHPAIN) + // e6y: compatibility optioned + if((default_comp[comp_ouchface]? + (plyr->health - st_oldhealth): + (st_oldhealth - plyr->health)) > ST_MUCHPAIN) + { + priority = 7; + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET; + } + else + { + priority = 6; + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset() + ST_RAMPAGEOFFSET; + } + + } + + } + + if (priority < 6) + { + // rapid firing + if (plyr->attackdown) + { + if (lastattackdown==-1) + lastattackdown = ST_RAMPAGEDELAY; + else if (!--lastattackdown) + { + priority = 5; + st_faceindex = ST_calcPainOffset() + ST_RAMPAGEOFFSET; + st_facecount = 1; + lastattackdown = 1; + } + } + else + lastattackdown = -1; + + } + + if (priority < 5) + { + // invulnerability + if ((plyr->cheats & CF_GODMODE) + || plyr->powers[pw_invulnerability]) + { + priority = 4; + + st_faceindex = ST_GODFACE; + st_facecount = 1; + + } + + } + + // look left or look right if the facecount has timed out + if (!st_facecount) + { + st_faceindex = ST_calcPainOffset() + (st_randomnumber % 3); + st_facecount = ST_STRAIGHTFACECOUNT; + priority = 0; + } + + st_facecount--; + +} + +int sts_traditional_keys; // killough 2/28/98: traditional status bar keys + +static void ST_updateWidgets(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_updateNum(&w_ready, true); + // refresh weapon change + // } + + // update keycard multiple widgets + for (i=0;i<3;i++) + { + keyboxes[i] = plyr->cards[i] ? i : -1; + + //jff 2/24/98 select double key + //killough 2/28/98: preserve traditional keys by config option + + if (plyr->cards[i+3]) + keyboxes[i] = keyboxes[i]==-1 || sts_traditional_keys ? i+3 : i+6; + } + + // refresh everything if this is him coming back to life + ST_updateFaceWidget(); + + // used by the w_armsbg widget + st_notdeathmatch = !deathmatch; + + // used by w_arms[] widgets + st_armson = st_statusbaron && !deathmatch; + + // used by w_frags widget + st_fragson = deathmatch && st_statusbaron; + st_fragscount = 0; + + for (i=0 ; ifrags[i]; + else + st_fragscount -= plyr->frags[i]; + } + + // get rid of chat window if up because of message + if (!--st_msgcounter) + st_chat = st_oldchat; + +} + +void ST_Ticker(void) +{ + st_clock++; + st_randomnumber = M_Random(); + ST_updateWidgets(); + st_oldhealth = plyr->health; +} + +int st_palette = 0; + +static void ST_doPaletteStuff(void) +{ + int palette; + int cnt = palette_ondamage ? plyr->damagecount : 0; + + if (palette_onpowers && plyr->powers[pw_strength]) + { + // slowly fade the berzerk out + int bzc = 12 - (plyr->powers[pw_strength]>>6); + if (bzc > cnt) + cnt = bzc; + } + + if (cnt) + { + palette = (cnt+7)>>3; + if (palette >= NUMREDPALS) + palette = NUMREDPALS-1; + + /* cph 2006/08/06 - if in the menu, reduce the red tint - navigating to + * load a game can be tricky if the screen is all red */ + if (menuactive) palette >>=1; + + palette += STARTREDPALS; + } + else + if (palette_onbonus && plyr->bonuscount) + { + palette = (plyr->bonuscount+7)>>3; + if (palette >= NUMBONUSPALS) + palette = NUMBONUSPALS-1; + palette += STARTBONUSPALS; + } + else + if (palette_onpowers && (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 (palette_onpowers && gamemission == chex + && palette >= STARTREDPALS && palette < STARTREDPALS + NUMREDPALS) + { + palette = RADIATIONPAL; + } + + if (palette != st_palette) { + V_SetPalette(st_palette = palette); // CPhipps - use new palette function + + // have to redraw the entire status bar when the palette changes + // in truecolor modes - POPE + if (V_GetMode() == VID_MODE15 || V_GetMode() == VID_MODE16 || V_GetMode() == VID_MODE32) + st_firsttime = true; + } +} + +void M_ChangeApplyPalette(void) +{ + if (gamestate == GS_LEVEL) + ST_doPaletteStuff(); +} + +static void ST_drawWidgets(dboolean refresh) +{ + int i; + int ammopct = 0; + + // used by w_arms[] widgets + st_armson = st_statusbaron && !deathmatch; + + // used by w_frags widget + st_fragson = deathmatch && st_statusbaron; + + //jff 2/16/98 make color of ammo depend on amount + if ((*w_ready.num == plyr->maxammo[weaponinfo[w_ready.data].ammo]) || + (ammo_colour_behaviour == ammo_colour_behaviour_no && plyr->backpack && + *w_ready.num*2 >= plyr->maxammo[weaponinfo[w_ready.data].ammo])) + STlib_updateNum(&w_ready, CR_BLUE2, refresh); + else { + if (plyr->maxammo[weaponinfo[w_ready.data].ammo]) + ammopct = (*w_ready.num*100)/plyr->maxammo[weaponinfo[w_ready.data].ammo]; + if (plyr->backpack && ammo_colour_behaviour != ammo_colour_behaviour_yes) + ammopct *= 2; + if (ammopct < ammo_red) + STlib_updateNum(&w_ready, CR_RED, refresh); + else + if (ammopct < ammo_yellow) + STlib_updateNum(&w_ready, CR_GOLD, refresh); + else + STlib_updateNum(&w_ready, CR_GREEN, refresh); + } + for (i=0;i<4;i++) + { + STlib_updateNum(&w_ammo[i], CR_DEFAULT, refresh); //jff 2/16/98 no xlation + STlib_updateNum(&w_maxammo[i], CR_DEFAULT, refresh); + } + + //jff 2/16/98 make color of health depend on amount + if (*w_health.n.numarmortype >= 2) + STlib_updatePercent(&w_armor, CR_BLUE2, refresh); + else if (plyr->armortype == 1) + STlib_updatePercent(&w_armor, CR_GREEN, refresh); + else if (plyr->armortype == 0) + STlib_updatePercent(&w_armor, CR_RED, refresh); + } + else + { + //jff 2/16/98 make color of armor depend on amount + if (*w_armor.n.numweaponowned[i]; + + for (i=0;i<3;i++) + keyboxes[i] = -1; + + STlib_init(); +} + +static void ST_createWidgets(void) +{ + int i; + + // ready weapon ammo + STlib_initNum(&w_ready, + ST_AMMOX, + ST_AMMOY, + tallnum, + &plyr->ammo[weaponinfo[plyr->readyweapon].ammo], + &st_statusbaron, + ST_AMMOWIDTH ); + + // the last weapon type + w_ready.data = plyr->readyweapon; + + // health percentage + STlib_initPercent(&w_health, + ST_HEALTHX, + ST_HEALTHY, + tallnum, + &plyr->health, + &st_statusbaron, + &tallpercent); + + // arms background + STlib_initBinIcon(&w_armsbg, + ST_ARMSBGX, + ST_ARMSBGY, + &armsbg, + &st_notdeathmatch, + &st_statusbaron); + + // weapons owned + for(i=0;i<6;i++) + { + STlib_initMultIcon(&w_arms[i], + ST_ARMSX+(i%3)*ST_ARMSXSPACE, + ST_ARMSY+(i/3)*ST_ARMSYSPACE, + arms[i], (int *) &plyr->weaponowned[i+1], + &st_armson); + } + + // frags sum + STlib_initNum(&w_frags, + ST_FRAGSX, + ST_FRAGSY, + tallnum, + &st_fragscount, + &st_fragson, + ST_FRAGSWIDTH); + + // faces + STlib_initMultIcon(&w_faces, + ST_FACESX, + ST_FACESY, + faces, + &st_faceindex, + &st_statusbaron); + + // armor percentage - should be colored later + STlib_initPercent(&w_armor, + ST_ARMORX, + ST_ARMORY, + tallnum, + &plyr->armorpoints, + &st_statusbaron, &tallpercent); + + // keyboxes 0-2 + STlib_initMultIcon(&w_keyboxes[0], + ST_KEY0X, + ST_KEY0Y, + keys, + &keyboxes[0], + &st_statusbaron); + + STlib_initMultIcon(&w_keyboxes[1], + ST_KEY1X, + ST_KEY1Y, + keys, + &keyboxes[1], + &st_statusbaron); + + STlib_initMultIcon(&w_keyboxes[2], + ST_KEY2X, + ST_KEY2Y, + keys, + &keyboxes[2], + &st_statusbaron); + + // ammo count (all four kinds) + STlib_initNum(&w_ammo[0], + ST_AMMO0X, + ST_AMMO0Y, + shortnum, + &plyr->ammo[0], + &st_statusbaron, + ST_AMMO0WIDTH); + + STlib_initNum(&w_ammo[1], + ST_AMMO1X, + ST_AMMO1Y, + shortnum, + &plyr->ammo[1], + &st_statusbaron, + ST_AMMO1WIDTH); + + STlib_initNum(&w_ammo[2], + ST_AMMO2X, + ST_AMMO2Y, + shortnum, + &plyr->ammo[2], + &st_statusbaron, + ST_AMMO2WIDTH); + + STlib_initNum(&w_ammo[3], + ST_AMMO3X, + ST_AMMO3Y, + shortnum, + &plyr->ammo[3], + &st_statusbaron, + ST_AMMO3WIDTH); + + // max ammo count (all four kinds) + STlib_initNum(&w_maxammo[0], + ST_MAXAMMO0X, + ST_MAXAMMO0Y, + shortnum, + &plyr->maxammo[0], + &st_statusbaron, + ST_MAXAMMO0WIDTH); + + STlib_initNum(&w_maxammo[1], + ST_MAXAMMO1X, + ST_MAXAMMO1Y, + shortnum, + &plyr->maxammo[1], + &st_statusbaron, + ST_MAXAMMO1WIDTH); + + STlib_initNum(&w_maxammo[2], + ST_MAXAMMO2X, + ST_MAXAMMO2Y, + shortnum, + &plyr->maxammo[2], + &st_statusbaron, + ST_MAXAMMO2WIDTH); + + STlib_initNum(&w_maxammo[3], + ST_MAXAMMO3X, + ST_MAXAMMO3Y, + shortnum, + &plyr->maxammo[3], + &st_statusbaron, + ST_MAXAMMO3WIDTH); +} + +static dboolean st_stopped = true; + +void ST_Start(void) +{ + if (!st_stopped) + ST_Stop(); + ST_initData(); + ST_createWidgets(); + st_stopped = false; +} + +static void ST_Stop(void) +{ + if (st_stopped) + return; + V_SetPalette(0); + st_stopped = true; +} + +void ST_Init(void) +{ + veryfirsttime = 0; + ST_loadData(); +} diff --git a/src/st_stuff.h b/src/st_stuff.h new file mode 100644 index 0000000..7fd8050 --- /dev/null +++ b/src/st_stuff.h @@ -0,0 +1,127 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Status bar code. + * Does the face/direction indicator animatin. + * Does palette indicators as well (red pain/berserk, bright pickup) + * + *-----------------------------------------------------------------------------*/ + +#ifndef __STSTUFF_H__ +#define __STSTUFF_H__ + +#include "doomtype.h" +#include "d_event.h" +#include "r_defs.h" + +// Size of statusbar. +// Now sensitive for scaling. + +// proff 08/18/98: Changed for high-res +#define ST_HEIGHT 32 +#define ST_WIDTH 320 +#define ST_Y (200 - ST_HEIGHT) + +// e6y: wide-res +extern int ST_SCALED_HEIGHT; +extern int ST_SCALED_WIDTH; +extern int ST_SCALED_Y; +extern int ST_SCALED_OFFSETX; + +void ST_SetScaledWidth(void); + +// +// STATUS BAR +// + +// Called by main loop. +dboolean ST_Responder(event_t* ev); + +// Called by main loop. +void ST_Ticker(void); + +// Called by main loop. +void ST_Drawer(dboolean st_statusbaron, dboolean refresh, dboolean fullmenu); + +// Called when the console player is spawned on each level. +void ST_Start(void); + +// Called by startup code. +void ST_Init(void); + +// After changing videomode; +void ST_SetResolution(void); + +// States for status bar code. +typedef enum +{ + AutomapState, + FirstPersonState +} st_stateenum_t; + +// States for the chat code. +typedef enum +{ + StartChatState, + WaitDestState, + GetChatState +} st_chatstateenum_t; + +// killough 5/2/98: moved from m_misc.c: + +extern int health_red; // health amount less than which status is red +extern int health_yellow; // health amount less than which status is yellow +extern int health_green; // health amount above is blue, below is green +extern int armor_red; // armor amount less than which status is red +extern int armor_yellow; // armor amount less than which status is yellow +extern int armor_green; // armor amount above is blue, below is green +extern int ammo_red; // ammo percent less than which status is red +extern int ammo_yellow; // ammo percent less is yellow more green +extern int sts_always_red;// status numbers do not change colors +extern int sts_pct_always_gray;// status percents do not change colors +extern int sts_traditional_keys; // display keys the traditional way +extern int sts_armorcolor_type; // armor color depends on type + +extern int st_palette; // cph 2006/04/06 - make palette visible + +typedef enum { + ammo_colour_behaviour_no, + ammo_colour_behaviour_full_only, + ammo_colour_behaviour_yes, + ammo_colour_behaviour_max +} ammo_colour_behaviour_t; +extern ammo_colour_behaviour_t ammo_colour_behaviour; +extern const char *ammo_colour_behaviour_list[]; + +// e6y: makes sense for wide resolutions +extern patchnum_t grnrock; +extern patchnum_t brdr_t, brdr_b, brdr_l, brdr_r; +extern patchnum_t brdr_tl, brdr_tr, brdr_bl, brdr_br; + +#endif diff --git a/src/statdump.c b/src/statdump.c new file mode 100644 index 0000000..60cb29d --- /dev/null +++ b/src/statdump.c @@ -0,0 +1,358 @@ + /* + + 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. + + */ + +#include +#include +#include + +#include "doomdef.h" +#include "d_player.h" +#include "m_argv.h" + +#include "statdump.h" + +#include "m_io.h" + +/* 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. + +#define MAX_CAPTURES 32 +static wbstartstruct_t captured_stats[MAX_CAPTURES]; +static int num_captured_stats = 0; + +static GameMission_t discovered_gamemission = none; + +/* 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 DiscoverGamemode(const wbstartstruct_t *stats, int num_stats) +{ + int partime; + int level; + int i; + + if (discovered_gamemission != none) + { + return; + } + + for (i=0; i 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 GetNumPlayers(const wbstartstruct_t *stats) +{ + int i; + int num_players = 0; + + for (i=0; iplyr[i].in) + { + ++num_players; + } + } + + return num_players; +} + +static void PrintBanner(FILE *stream) +{ + fprintf(stream, "===========================================\n"); +} + +static void PrintPercentage(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 PrintPlayerStats(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: "); + PrintPercentage(stream, player->skills, stats->maxkills); + fprintf(stream, "\n"); + + /* Items percentage */ + + fprintf(stream, "\tItems: "); + PrintPercentage(stream, player->sitems, stats->maxitems); + fprintf(stream, "\n"); + + /* Secrets percentage */ + + fprintf(stream, "\tSecrets: "); + PrintPercentage(stream, player->ssecret, stats->maxsecret); + fprintf(stream, "\n"); +} + +/* Frags table for multiplayer games. */ + +static void PrintFragsTable(FILE *stream, const wbstartstruct_t *stats) +{ + int x, y; + + fprintf(stream, "Frags:\n"); + + /* Print header */ + + fprintf(stream, "\t\t"); + + for (x=0; xplyr[x].in) + { + continue; + } + + fprintf(stream, "%s\t", player_colors[x]); + } + + fprintf(stream, "\n"); + + fprintf(stream, "\t\t-------------------------------- VICTIMS\n"); + + /* Print table */ + + for (y=0; yplyr[y].in) + { + continue; + } + + fprintf(stream, "\t%s\t|", player_colors[y]); + + for (x=0; xplyr[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 PrintLevelName(FILE *stream, int episode, int level) +{ + PrintBanner(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; + } + + PrintBanner(stream); +} + +/* Print details of a statistics buffer to the given file. */ + +static void PrintStats(FILE *stream, const wbstartstruct_t *stats) +{ + short leveltime, partime; + int i; + + PrintLevelName(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; iplyr[i].in) + { + PrintPlayerStats(stream, stats, i); + } + } + + if (GetNumPlayers(stats) >= 2) + { + PrintFragsTable(stream, stats); + } + + fprintf(stream, "\n"); +} + +void StatCopy(const wbstartstruct_t *stats) +{ + if (M_CheckParm("-statdump") && num_captured_stats < MAX_CAPTURES) + { + memcpy(&captured_stats[num_captured_stats], stats, + sizeof(wbstartstruct_t)); + ++num_captured_stats; + } +} + +void StatDump(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_CheckParm("-statdump"); + + if (i > 0 && i < myargc-1) + { + 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. + + DiscoverGamemode(captured_stats, num_captured_stats); + + // Allow "-" as output file, for stdout. + + if (strcmp(myargv[i + 1], "-") != 0) + { + dumpfile = M_fopen(myargv[i + 1], "w"); + } + else + { + dumpfile = stdout; + } + + for (i = 0; i < num_captured_stats; ++i) + { + PrintStats(dumpfile, &captured_stats[i]); + } + + if (dumpfile != stdout) + { + fclose(dumpfile); + } + } +} + diff --git a/src/statdump.h b/src/statdump.h new file mode 100644 index 0000000..f4f11ef --- /dev/null +++ b/src/statdump.h @@ -0,0 +1,23 @@ + /* + + 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 + +void StatCopy(const wbstartstruct_t *stats); +void StatDump(void); + +#endif /* #ifndef DOOM_STATDUMP_H */ diff --git a/src/tables.c b/src/tables.c new file mode 100644 index 0000000..4322954 --- /dev/null +++ b/src/tables.c @@ -0,0 +1,138 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * 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. + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "w_wad.h" +#include "tables.h" + +// killough 5/3/98: reformatted + +int SlopeDiv(unsigned int num, unsigned int den) +{ + unsigned ans; + + if (den < 512) + return SLOPERANGE; + ans = (num<<3)/(den>>8); + return ans <= SLOPERANGE ? ans : SLOPERANGE; +} + +// [crispy] catch SlopeDiv overflows, only used in rendering +int SlopeDivEx(unsigned int num, unsigned int den) +{ + uint_64_t ans; + if (den < 512) + return SLOPERANGE; + ans = ((uint_64_t)num<<3)/(den>>8); + return ans <= SLOPERANGE ? (int)ans : SLOPERANGE; +} + +fixed_t finetangent[4096]; + +//const fixed_t *const finecosine = &finesine[FINEANGLES/4]; + +fixed_t finesine[10240]; + +angle_t tantoangle[2049]; + +#include "m_swap.h" +#include "lprintf.h" + +// R_LoadTrigTables +// Load trig tables from a wad file lump +// CPhipps 24/12/98 - fix endianness (!) +// +void R_LoadTrigTables(void) +{ + int lump; + { + lump = (W_CheckNumForName)("SINETABL",ns_prboom); + if (lump == -1) I_Error("Failed to locate trig tables"); + if (W_LumpLength(lump) != sizeof(finesine)) + I_Error("R_LoadTrigTables: Invalid SINETABL"); + W_ReadLump(lump,(unsigned char*)finesine); + } + { + lump = (W_CheckNumForName)("TANGTABL",ns_prboom); + if (lump == -1) I_Error("Failed to locate trig tables"); + if (W_LumpLength(lump) != sizeof(finetangent)) + I_Error("R_LoadTrigTables: Invalid TANGTABL"); + W_ReadLump(lump,(unsigned char*)finetangent); + } + { + lump = (W_CheckNumForName)("TANTOANG",ns_prboom); + if (lump == -1) I_Error("Failed to locate trig tables"); + if (W_LumpLength(lump) != sizeof(tantoangle)) + I_Error("R_LoadTrigTables: Invalid TANTOANG"); + W_ReadLump(lump,(unsigned char*)tantoangle); + } + // Endianness correction - might still be non-portable, but is fast where possible + { + size_t n; + lprintf(LO_INFO, "Endianness..."); + + // This test doesn't assume the endianness of the tables, but deduces them from + // en entry. I hope this is portable. + if ((10 < finesine[1]) && (finesine[1] < 100)) { + lprintf(LO_INFO, "ok."); + return; // Endianness is correct + } + + // Must correct endianness of every long loaded (!) +#define CORRECT_TABLE_ENDIAN(tbl) \ + for (n = 0; n +#include +#include +#include +#include "umapinfo.h" +#include "scanner.h" + +extern "C" +{ +#include "m_misc.h" +#include "g_game.h" +#include "doomdef.h" +#include "doomstat.h" + +void M_AddEpisode(const char *map, const char *gfx, const char *txt, const char *alpha); +void M_ClearEpisodes(void); + +MapList Maps; +} + + +//========================================================================== +// +// The Doom actors in their original order +// Names are the same as in ZDoom. +// +//========================================================================== + +static const char * const ActorNames[] = +{ + "DoomPlayer", + "ZombieMan", + "ShotgunGuy", + "Archvile", + "ArchvileFire", + "Revenant", + "RevenantTracer", + "RevenantTracerSmoke", + "Fatso", + "FatShot", + "ChaingunGuy", + "DoomImp", + "Demon", + "Spectre", + "Cacodemon", + "BaronOfHell", + "BaronBall", + "HellKnight", + "LostSoul", + "SpiderMastermind", + "Arachnotron", + "Cyberdemon", + "PainElemental", + "WolfensteinSS", + "CommanderKeen", + "BossBrain", + "BossEye", + "BossTarget", + "SpawnShot", + "SpawnFire", + "ExplosiveBarrel", + "DoomImpBall", + "CacodemonBall", + "Rocket", + "PlasmaBall", + "BFGBall", + "ArachnotronPlasma", + "BulletPuff", + "Blood", + "TeleportFog", + "ItemFog", + "TeleportDest", + "BFGExtra", + "GreenArmor", + "BlueArmor", + "HealthBonus", + "ArmorBonus", + "BlueCard", + "RedCard", + "YellowCard", + "YellowSkull", + "RedSkull", + "BlueSkull", + "Stimpack", + "Medikit", + "Soulsphere", + "InvulnerabilitySphere", + "Berserk", + "BlurSphere", + "RadSuit", + "Allmap", + "Infrared", + "Megasphere", + "Clip", + "ClipBox", + "RocketAmmo", + "RocketBox", + "Cell", + "CellPack", + "Shell", + "ShellBox", + "Backpack", + "BFG9000", + "Chaingun", + "Chainsaw", + "RocketLauncher", + "PlasmaRifle", + "Shotgun", + "SuperShotgun", + "TechLamp", + "TechLamp2", + "Column", + "TallGreenColumn", + "ShortGreenColumn", + "TallRedColumn", + "ShortRedColumn", + "SkullColumn", + "HeartColumn", + "EvilEye", + "FloatingSkull", + "TorchTree", + "BlueTorch", + "GreenTorch", + "RedTorch", + "ShortBlueTorch", + "ShortGreenTorch", + "ShortRedTorch", + "Stalagtite", + "TechPillar", + "CandleStick", + "Candelabra", + "BloodyTwitch", + "Meat2", + "Meat3", + "Meat4", + "Meat5", + "NonsolidMeat2", + "NonsolidMeat4", + "NonsolidMeat3", + "NonsolidMeat5", + "NonsolidTwitch", + "DeadCacodemon", + "DeadMarine", + "DeadZombieMan", + "DeadDemon", + "DeadLostSoul", + "DeadDoomImp", + "DeadShotgunGuy", + "GibbedMarine", + "GibbedMarineExtra", + "HeadsOnAStick", + "Gibs", + "HeadOnAStick", + "HeadCandles", + "DeadStick", + "LiveStick", + "BigTree", + "BurningBarrel", + "HangNoGuts", + "HangBNoBrain", + "HangTLookingDown", + "HangTSkull", + "HangTLookingUp", + "HangTNoBrain", + "ColonGibs", + "SmallBloodPool", + "BrainStem", + //Boom/MBF additions + "PointPusher", + "PointPuller", + "MBFHelperDog", + "PlasmaBall1", + "PlasmaBall2", + "EvilSceptre", + "UnholyBible", + "MusicChanger", + "Deh_Actor_145", + "Deh_Actor_146", + "Deh_Actor_147", + "Deh_Actor_148", + "Deh_Actor_149", + // DEHEXTRA Actors start here + "Deh_Actor_150", // Extra thing 0 + "Deh_Actor_151", // Extra thing 1 + "Deh_Actor_152", // Extra thing 2 + "Deh_Actor_153", // Extra thing 3 + "Deh_Actor_154", // Extra thing 4 + "Deh_Actor_155", // Extra thing 5 + "Deh_Actor_156", // Extra thing 6 + "Deh_Actor_157", // Extra thing 7 + "Deh_Actor_158", // Extra thing 8 + "Deh_Actor_159", // Extra thing 9 + "Deh_Actor_160", // Extra thing 10 + "Deh_Actor_161", // Extra thing 11 + "Deh_Actor_162", // Extra thing 12 + "Deh_Actor_163", // Extra thing 13 + "Deh_Actor_164", // Extra thing 14 + "Deh_Actor_165", // Extra thing 15 + "Deh_Actor_166", // Extra thing 16 + "Deh_Actor_167", // Extra thing 17 + "Deh_Actor_168", // Extra thing 18 + "Deh_Actor_169", // Extra thing 19 + "Deh_Actor_170", // Extra thing 20 + "Deh_Actor_171", // Extra thing 21 + "Deh_Actor_172", // Extra thing 22 + "Deh_Actor_173", // Extra thing 23 + "Deh_Actor_174", // Extra thing 24 + "Deh_Actor_175", // Extra thing 25 + "Deh_Actor_176", // Extra thing 26 + "Deh_Actor_177", // Extra thing 27 + "Deh_Actor_178", // Extra thing 28 + "Deh_Actor_179", // Extra thing 29 + "Deh_Actor_180", // Extra thing 30 + "Deh_Actor_181", // Extra thing 31 + "Deh_Actor_182", // Extra thing 32 + "Deh_Actor_183", // Extra thing 33 + "Deh_Actor_184", // Extra thing 34 + "Deh_Actor_185", // Extra thing 35 + "Deh_Actor_186", // Extra thing 36 + "Deh_Actor_187", // Extra thing 37 + "Deh_Actor_188", // Extra thing 38 + "Deh_Actor_189", // Extra thing 39 + "Deh_Actor_190", // Extra thing 40 + "Deh_Actor_191", // Extra thing 41 + "Deh_Actor_192", // Extra thing 42 + "Deh_Actor_193", // Extra thing 43 + "Deh_Actor_194", // Extra thing 44 + "Deh_Actor_195", // Extra thing 45 + "Deh_Actor_196", // Extra thing 46 + "Deh_Actor_197", // Extra thing 47 + "Deh_Actor_198", // Extra thing 48 + "Deh_Actor_199", // Extra thing 49 + "Deh_Actor_200", // Extra thing 50 + "Deh_Actor_201", // Extra thing 51 + "Deh_Actor_202", // Extra thing 52 + "Deh_Actor_203", // Extra thing 53 + "Deh_Actor_204", // Extra thing 54 + "Deh_Actor_205", // Extra thing 55 + "Deh_Actor_206", // Extra thing 56 + "Deh_Actor_207", // Extra thing 57 + "Deh_Actor_208", // Extra thing 58 + "Deh_Actor_209", // Extra thing 59 + "Deh_Actor_210", // Extra thing 60 + "Deh_Actor_211", // Extra thing 61 + "Deh_Actor_212", // Extra thing 62 + "Deh_Actor_213", // Extra thing 63 + "Deh_Actor_214", // Extra thing 64 + "Deh_Actor_215", // Extra thing 65 + "Deh_Actor_216", // Extra thing 66 + "Deh_Actor_217", // Extra thing 67 + "Deh_Actor_218", // Extra thing 68 + "Deh_Actor_219", // Extra thing 69 + "Deh_Actor_220", // Extra thing 70 + "Deh_Actor_221", // Extra thing 71 + "Deh_Actor_222", // Extra thing 72 + "Deh_Actor_223", // Extra thing 73 + "Deh_Actor_224", // Extra thing 74 + "Deh_Actor_225", // Extra thing 75 + "Deh_Actor_226", // Extra thing 76 + "Deh_Actor_227", // Extra thing 77 + "Deh_Actor_228", // Extra thing 78 + "Deh_Actor_229", // Extra thing 79 + "Deh_Actor_230", // Extra thing 80 + "Deh_Actor_231", // Extra thing 81 + "Deh_Actor_232", // Extra thing 82 + "Deh_Actor_233", // Extra thing 83 + "Deh_Actor_234", // Extra thing 84 + "Deh_Actor_235", // Extra thing 85 + "Deh_Actor_236", // Extra thing 86 + "Deh_Actor_237", // Extra thing 87 + "Deh_Actor_238", // Extra thing 88 + "Deh_Actor_239", // Extra thing 89 + "Deh_Actor_240", // Extra thing 90 + "Deh_Actor_241", // Extra thing 91 + "Deh_Actor_242", // Extra thing 92 + "Deh_Actor_243", // Extra thing 93 + "Deh_Actor_244", // Extra thing 94 + "Deh_Actor_245", // Extra thing 95 + "Deh_Actor_246", // Extra thing 96 + "Deh_Actor_247", // Extra thing 97 + "Deh_Actor_248", // Extra thing 98 + "Deh_Actor_249", // Extra thing 99 + NULL +}; + + +// ----------------------------------------------- +// +// +// ----------------------------------------------- + +static void FreeMap(MapEntry *mape) +{ + if (mape->mapname) free(mape->mapname); + if (mape->levelname) free(mape->levelname); + if (mape->label) free(mape->label); + if (mape->author) free(mape->author); + if (mape->intertext) free(mape->intertext); + if (mape->intertextsecret) free(mape->intertextsecret); + if (mape->properties) free(mape->properties); + if (mape->bossactions) free(mape->bossactions); + mape->propertycount = 0; + mape->mapname = NULL; + mape->properties = NULL; +} + + +void FreeMapList() +{ + unsigned i; + + for(i = 0; i < Maps.mapcount; i++) + { + FreeMap(&Maps.maps[i]); + } + free(Maps.maps); + Maps.maps = NULL; + Maps.mapcount = 0; +} + + +void ReplaceString(char **pptr, const char *newstring) +{ + if (*pptr != NULL) free(*pptr); + *pptr = strdup(newstring); +} + +// ----------------------------------------------- +// +// Parses a set of string and concatenates them +// +// ----------------------------------------------- + +static char *ParseMultiString(Scanner &scanner, int error) +{ + char *build = NULL; + + if (scanner.CheckToken(TK_Identifier)) + { + if (!stricmp(scanner.string, "clear")) + { + return strdup("-"); // this was explicitly deleted to override the default. + } + else + { + scanner.ErrorF("Either 'clear' or string constant expected"); + } + } + + do + { + scanner.MustGetToken(TK_StringConst); + if (build == NULL) build = strdup(scanner.string); + else + { + size_t newlen = strlen(build) + strlen(scanner.string) + 2; // strlen for both the existing text and the new line, plus room for one \n and one \0 + build = (char*)realloc(build, newlen); // Prepare the destination memory for the below strcats + strcat(build, "\n"); // Replace the existing text's \0 terminator with a \n + strcat(build, scanner.string); // Concatenate the new line onto the existing text + } + } while (scanner.CheckToken(',')); + return build; +} + +// ----------------------------------------------- +// +// Parses a lump name. The buffer must be at least 9 characters. +// +// ----------------------------------------------- + +static int ParseLumpName(Scanner &scanner, char *buffer) +{ + scanner.MustGetToken(TK_StringConst); + if (strlen(scanner.string) > 8) + { + scanner.ErrorF("String too long. Maximum size is 8 characters."); + return 0; + } + strncpy(buffer, scanner.string, 8); + buffer[8] = 0; + M_Strupr(buffer); + return 1; +} + +// ----------------------------------------------- +// +// Parses a standard property that is already known +// These do not get stored in the property list +// but in dedicated struct member variables. +// +// ----------------------------------------------- + +static int ParseStandardProperty(Scanner &scanner, MapEntry *mape) +{ + // find the next line with content. + // this line is no property. + + scanner.MustGetToken(TK_Identifier); + char *pname = strdup(scanner.string); + scanner.MustGetToken('='); + + if (!stricmp(pname, "levelname")) + { + scanner.MustGetToken(TK_StringConst); + ReplaceString(&mape->levelname, scanner.string); + } + else if (!stricmp(pname, "label")) + { + if (scanner.CheckToken(TK_Identifier)) + { + if (!stricmp(scanner.string, "clear")) ReplaceString(&mape->label, "-"); + else + { + scanner.ErrorF("Either 'clear' or string constant expected"); + return 0; + } + } + else + { + scanner.MustGetToken(TK_StringConst); + ReplaceString(&mape->label, scanner.string); + } + } + else if (!stricmp(pname, "next")) + { + ParseLumpName(scanner, mape->nextmap); + if (!G_ValidateMapName(mape->nextmap, NULL, NULL)) + { + scanner.ErrorF("Invalid map name %s.", mape->nextmap); + return 0; + } + } + else if (!stricmp(pname, "nextsecret")) + { + ParseLumpName(scanner, mape->nextsecret); + if (!G_ValidateMapName(mape->nextsecret, NULL, NULL)) + { + scanner.ErrorF("Invalid map name %s", mape->nextsecret); + return 0; + } + } + else if (!stricmp(pname, "author")) + { + scanner.MustGetToken(TK_StringConst); + ReplaceString(&mape->author, scanner.string); + } + else if (!stricmp(pname, "levelpic")) + { + ParseLumpName(scanner, mape->levelpic); + } + else if (!stricmp(pname, "skytexture")) + { + ParseLumpName(scanner, mape->skytexture); + } + else if (!stricmp(pname, "music")) + { + ParseLumpName(scanner, mape->music); + } + else if (!stricmp(pname, "endpic")) + { + ParseLumpName(scanner, mape->endpic); + } + else if (!stricmp(pname, "endcast")) + { + scanner.MustGetToken(TK_BoolConst); + if (scanner.boolean) strcpy(mape->endpic, "$CAST"); + else strcpy(mape->endpic, "-"); + } + else if (!stricmp(pname, "endbunny")) + { + scanner.MustGetToken(TK_BoolConst); + if (scanner.boolean) strcpy(mape->endpic, "$BUNNY"); + else strcpy(mape->endpic, "-"); + } + else if (!stricmp(pname, "endgame")) + { + scanner.MustGetToken(TK_BoolConst); + if (scanner.boolean) strcpy(mape->endpic, "!"); + else strcpy(mape->endpic, "-"); + } + else if (!stricmp(pname, "exitpic")) + { + ParseLumpName(scanner, mape->exitpic); + } + else if (!stricmp(pname, "enterpic")) + { + ParseLumpName(scanner, mape->enterpic); + } + else if (!stricmp(pname, "nointermission")) + { + scanner.MustGetToken(TK_BoolConst); + mape->nointermission = scanner.boolean; + } + else if (!stricmp(pname, "partime")) + { + scanner.MustGetInteger(); + mape->partime = TICRATE * scanner.number; + } + else if (!stricmp(pname, "intertext")) + { + char *lname = ParseMultiString(scanner, 1); + if (!lname) return 0; + if (mape->intertext != NULL) free(mape->intertext); + mape->intertext = lname; + } + else if (!stricmp(pname, "intertextsecret")) + { + char *lname = ParseMultiString(scanner, 1); + if (!lname) return 0; + if (mape->intertextsecret != NULL) free(mape->intertextsecret); + mape->intertextsecret = lname; + } + else if (!stricmp(pname, "interbackdrop")) + { + ParseLumpName(scanner, mape->interbackdrop); + } + else if (!stricmp(pname, "intermusic")) + { + ParseLumpName(scanner, mape->intermusic); + } + else if (!stricmp(pname, "episode")) + { + if (scanner.CheckToken(TK_Identifier)) + { + if (!stricmp(scanner.string, "clear")) M_ClearEpisodes(); + else + { + scanner.ErrorF("Either 'clear' or string constant expected"); + return 0; + } + } + else + { + char lumpname[9] = {0}; + char *alttext = NULL; + char *key = NULL; + + ParseLumpName(scanner, lumpname); + if (scanner.CheckToken(',')) + { + scanner.MustGetToken(TK_StringConst); + alttext = strdup(scanner.string); + if (scanner.CheckToken(',')) + { + scanner.MustGetToken(TK_StringConst); + key = strdup(scanner.string); + key[0] = tolower(key[0]); + } + } + + M_AddEpisode(mape->mapname, lumpname, alttext, key); + + if (alttext) free(alttext); + if (key) free(key); + } + } + else if (!stricmp(pname, "bossaction")) + { + scanner.MustGetToken(TK_Identifier); + int classnum, special, tag; + if (!stricmp(scanner.string, "clear")) + { + // mark level free of boss actions + classnum = special = tag = -1; + if (mape->bossactions) free(mape->bossactions); + mape->bossactions = NULL; + mape->numbossactions = -1; + } + else + { + int i; + for (i = 0; ActorNames[i]; i++) + { + if (!stricmp(scanner.string, ActorNames[i])) break; + } + if (ActorNames[i] == NULL) + { + scanner.ErrorF("Unknown thing type %s", scanner.string); + return 0; + } + + scanner.MustGetToken(','); + scanner.MustGetInteger(); + special = scanner.number; + scanner.MustGetToken(','); + scanner.MustGetInteger(); + tag = scanner.number; + // allow no 0-tag specials here, unless a level exit. + if (tag != 0 || special == 11 || special == 51 || special == 52 || special == 124) + { + if (mape->numbossactions == -1) mape->numbossactions = 1; + else mape->numbossactions++; + mape->bossactions = (struct BossAction *)realloc(mape->bossactions, sizeof(struct BossAction) * mape->numbossactions); + mape->bossactions[mape->numbossactions - 1].type = i; + mape->bossactions[mape->numbossactions - 1].special = special; + mape->bossactions[mape->numbossactions - 1].tag = tag; + } + + } + } + else + { + do + { + if (!scanner.CheckFloat()) scanner.GetNextToken(); + if (scanner.token > TK_BoolConst) + { + scanner.Error(TK_Identifier); + } + + } while (scanner.CheckToken(',')); + } + free(pname); + return 1; +} + +// ----------------------------------------------- +// +// Parses a complete map entry +// +// ----------------------------------------------- + +static int ParseMapEntry(Scanner &scanner, MapEntry *val) +{ + val->mapname = NULL; + val->propertycount = 0; + val->properties = NULL; + + scanner.MustGetIdentifier("map"); + scanner.MustGetToken(TK_Identifier); + if (!G_ValidateMapName(scanner.string, NULL, NULL)) + { + scanner.ErrorF("Invalid map name %s", scanner.string); + return 0; + } + + ReplaceString(&val->mapname, scanner.string); + scanner.MustGetToken('{'); + while(!scanner.CheckToken('}')) + { + ParseStandardProperty(scanner, val); + } + return 1; +} + +// ----------------------------------------------- +// +// Parses a complete UMAPINFO lump +// +// ----------------------------------------------- + +int ParseUMapInfo(const unsigned char *buffer, size_t length, umapinfo_errorfunc err) +{ + Scanner scanner((const char*)buffer, length); + unsigned int i; + + scanner.SetErrorCallback(err); + + + while (scanner.TokensLeft()) + { + MapEntry parsed = { 0 }; + ParseMapEntry(scanner, &parsed); + + // Set default level progression here to simplify the checks elsewhere. Doing this lets us skip all normal code for this if nothing has been defined. + if (parsed.endpic[0] && (strcmp(parsed.endpic, "-") != 0)) + { + parsed.nextmap[0] = 0; + } + else if (!parsed.nextmap[0] && !parsed.endpic[0]) + { + if (!stricmp(parsed.mapname, "MAP30")) strcpy(parsed.endpic, "$CAST"); + else if (!stricmp(parsed.mapname, "E1M8")) strcpy(parsed.endpic, gamemode == retail? "CREDIT" : "HELP2"); + else if (!stricmp(parsed.mapname, "E2M8")) strcpy(parsed.endpic, "VICTORY2"); + else if (!stricmp(parsed.mapname, "E3M8")) strcpy(parsed.endpic, "$BUNNY"); + else if (!stricmp(parsed.mapname, "E4M8")) strcpy(parsed.endpic, "ENDPIC"); + else if (gamemission == chex && !stricmp(parsed.mapname, "E1M5")) strcpy(parsed.endpic, "CREDIT"); + else + { + int ep, map; + G_ValidateMapName(parsed.mapname, &ep, &map); + map++; + if (gamemode == commercial) + sprintf(parsed.nextmap, "MAP%02d", map); + else + sprintf(parsed.nextmap, "E%dM%d", ep, map); + } + } + + // Does this property already exist? If yes, replace it. + for(i = 0; i < Maps.mapcount; i++) + { + if (!strcmp(parsed.mapname, Maps.maps[i].mapname)) + { + FreeMap(&Maps.maps[i]); + Maps.maps[i] = parsed; + break; + } + } + // Not found so create a new one. + if (i == Maps.mapcount) + { + Maps.mapcount++; + Maps.maps = (MapEntry*)realloc(Maps.maps, sizeof(MapEntry)*Maps.mapcount); + Maps.maps[Maps.mapcount-1] = parsed; + } + + } + return 1; +} + + +MapProperty *FindProperty(MapEntry *map, const char *name) +{ + return NULL; +} diff --git a/src/umapinfo.h b/src/umapinfo.h new file mode 100644 index 0000000..22d77b1 --- /dev/null +++ b/src/umapinfo.h @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2017 Christoph Oelckers +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT 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, see http://www.gnu.org/licenses/ +// +//----------------------------------------------------------------------------- + +#ifndef __UMAPINFO_H +#define __UMAPINFO_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct BossAction +{ + int type; + int special; + int tag; +}; + +struct MapEntry +{ + char *mapname; + char *levelname; + char *label; + char *author; + char *intertext; + char *intertextsecret; + char levelpic[9]; + char nextmap[9]; + char nextsecret[9]; + char music[9]; + char skytexture[9]; + char endpic[9]; + char exitpic[9]; + char enterpic[9]; + char interbackdrop[9]; + char intermusic[9]; + int partime; + int nointermission; + int numbossactions; + + unsigned int propertycount; + struct MapProperty *properties; + struct BossAction *bossactions; +}; + +struct MapList +{ + unsigned int mapcount; + struct MapEntry *maps; +}; + +typedef void (*umapinfo_errorfunc)(const char *fmt, ...); // this must not return! + +extern struct MapList Maps; + +int ParseUMapInfo(const unsigned char *buffer, size_t length, umapinfo_errorfunc err); +void FreeMapList(); +struct MapProperty *FindProperty(struct MapEntry *map, const char *name); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/v_video.c b/src/v_video.c new file mode 100644 index 0000000..51f77d0 --- /dev/null +++ b/src/v_video.c @@ -0,0 +1,1871 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Gamma correction LUT stuff. + * Color range translation support + * Functions to draw patches (by post) directly to screen. + * Functions to blit a block to the screen. + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "SDL.h" + +#include "doomdef.h" +#include "doomstat.h" +#include "r_main.h" +#include "r_draw.h" +#include "m_bbox.h" +#include "w_wad.h" /* needed for color translation lump lookup */ +#include "v_video.h" +#include "i_video.h" +#include "r_filter.h" +#include "lprintf.h" +#include "st_stuff.h" +#include "e6y.h" + +// DWF 2012-05-10 +// SetRatio sets the following global variables based on window geometry and +// user preferences. The integer ratio is hardly used anymore, so further +// simplification may be in order. +dboolean tallscreen; +unsigned int ratio_multiplier, ratio_scale; +float gl_ratio; +int psprite_offset; // Needed for "tallscreen" modes + +const char *render_aspects_list[5] = {"auto", "16:9", "16:10", "4:3", "5:4"}; +const char *render_stretch_list[patch_stretch_max] = {"not adjusted", "Doom format", "fit to width"}; + +stretch_param_t stretch_params_table[3][VPT_ALIGN_MAX]; +stretch_param_t *stretch_params; + +cb_video_t video; +cb_video_t video_stretch; +cb_video_t video_full; +int patches_scalex; +int patches_scaley; + +int render_stretch_hud; +int render_stretch_hud_default; +int render_patches_scalex; +int render_patches_scaley; + +// Each screen is [SCREENWIDTH*SCREENHEIGHT]; +screeninfo_t screens[NUM_SCREENS]; + +/* jff 4/24/98 initialize this at runtime */ +const byte *colrngs[CR_LIMIT]; + +int usegamma; + +/* + * V_InitColorTranslation + * + * Loads the color translation tables from predefined lumps at game start + * No return + * + * Used for translating text colors from the red palette range + * to other colors. The first nine entries can be used to dynamically + * switch the output of text color thru the HUlib_drawText routine + * by embedding ESCn in the text to obtain color n. Symbols for n are + * provided in v_video.h. + * + * cphipps - constness of crdef_t stuff fixed + */ + +typedef struct { + const char *name; + const byte **map; +} crdef_t; + +// killough 5/2/98: table-driven approach +static const crdef_t crdefs[] = { + {"CRBRICK", &colrngs[CR_BRICK ]}, + {"CRTAN", &colrngs[CR_TAN ]}, + {"CRGRAY", &colrngs[CR_GRAY ]}, + {"CRGREEN", &colrngs[CR_GREEN ]}, + {"CRBROWN", &colrngs[CR_BROWN ]}, + {"CRGOLD", &colrngs[CR_GOLD ]}, + {"CRRED", &colrngs[CR_RED ]}, + {"CRBLUE", &colrngs[CR_BLUE ]}, + {"CRORANGE", &colrngs[CR_ORANGE]}, + {"CRYELLOW", &colrngs[CR_YELLOW]}, + {"CRBLUE2", &colrngs[CR_BLUE2]}, + {"CRBLACK", &colrngs[CR_BLACK]}, + {"CRPURPLE", &colrngs[CR_PURPLE]}, + {"CRWHITE", &colrngs[CR_WHITE]}, + {NULL} +}; + +// [FG] translate between blood color value as per EE spec +// and actual color translation table index + +static const int bloodcolor[] = { + CR_RED, // 0 - Red (normal) + CR_GRAY, // 1 - Grey + CR_GREEN, // 2 - Green + CR_BLUE2, // 3 - Blue + CR_YELLOW, // 4 - Yellow + CR_BLACK, // 5 - Black + CR_PURPLE, // 6 - Purple + CR_WHITE, // 7 - White + CR_ORANGE, // 8 - Orange +}; + +int V_BloodColor(int blood) +{ + if (blood < 0 || blood > 8) + blood = 0; + + return bloodcolor[blood]; +} + +// haleyjd: DOSDoom-style single translucency lookup-up table +// generation code. This code has a 32k (plus a bit more) +// footprint but allows a much wider range of translucency effects +// than BOOM-style translucency. This will be used for particles, +// for variable mapthing trans levels, and for screen patches. + +// haleyjd: Updated 06/21/08 to use 32k lookup, mainly to fix +// additive translucency. Note this code is included in Odamex and +// so it can be considered GPL as used here, rather than BSD. But, +// I don't care either way. It is effectively dual-licensed I suppose. + +unsigned int Col2RGB8[65][256]; +byte RGB32k[32][32][32]; + +#define MAKECOLOR(a) (((a)<<3)|((a)>>2)) + +void V_InitFlexTranTable(void) +{ + static int flexTranInit = false; + + if (!flexTranInit) + { + int r, g, b, x, y, pos; + const unsigned char *palette = V_GetPlaypal(); + + // mark that we've initialized the flex tran table + flexTranInit = true; + + // build RGB table + for(r = 0; r < 32; r++) + { + for(g = 0; g < 32; g++) + { + for(b = 0; b < 32; b++) + { + RGB32k[r][g][b] = V_BestColor(palette, + MAKECOLOR(r), MAKECOLOR(g), MAKECOLOR(b)); + } + } + } + + // build lookup table + for(x = 0; x < 65; x++) + { + pos = 0; + for(y = 0; y < 256; y++) + { + Col2RGB8[x][y] = + (((palette[pos + 0] * x) >> 4) << 20) | + ((palette[pos + 1] * x) >> 4) | + (((palette[pos + 2] * x) >> 4) << 10); + + pos += 3; + } + } + } +} + +// killough 5/2/98: tiny engine driven by table above +void V_InitColorTranslation(void) +{ + register const crdef_t *p; + for (p=crdefs; p->name; p++) + { + *p->map = W_CacheLumpName(p->name); + if (p - crdefs == CR_DEFAULT) + continue; + if (gamemission == chex || gamemission == hacx) + { + byte *temp = malloc(256); + memcpy (temp, *p->map, 256); + if (gamemission == chex) + memcpy (temp+112, *p->map+176, 16); // green range + else if (gamemission == hacx) + memcpy (temp+192, *p->map+176, 16); // blue range + *p->map = temp; + } + } +} + +// +// V_CopyRect +// +// Copies a source rectangle in a screen buffer to a destination +// rectangle in another screen buffer. Source origin in srcx,srcy, +// destination origin in destx,desty, common size in width and height. +// Source buffer specfified by srcscrn, destination buffer by destscrn. +// +// Marks the destination rectangle on the screen dirty. +// +// No return. +// +static void FUNC_V_CopyRect(int srcscrn, int destscrn, + int x, int y, int width, int height, + enum patch_translation_e flags) +{ + byte *src; + byte *dest; + int pixel_depth = V_GetPixelDepth(); + + if (flags & VPT_STRETCH_MASK) + { + stretch_param_t *params; + int sx = x; + int sy = y; + + params = &stretch_params[flags & VPT_ALIGN_MASK]; + + x = params->video->x1lookup[x]; + y = params->video->y1lookup[y]; + width = params->video->x2lookup[sx + width - 1] - x + 1; + height = params->video->y2lookup[sy + height - 1] - y + 1; + x += params->deltax1; + y += params->deltay1; + } + + if (x < 0) + { + width += x; + x = 0; + } + + if (x + width > SCREENWIDTH) + { + width = SCREENWIDTH - x; + } + + if (y < 0) + { + height += y; + y = 0; + } + + if (y + height > SCREENHEIGHT) + { + height = SCREENHEIGHT - y; + } + + if (width <= 0 || height <= 0) + { + return; + } + + src = screens[srcscrn].data + screens[srcscrn].byte_pitch * y + x * pixel_depth; + dest = screens[destscrn].data + screens[destscrn].byte_pitch * y + x * pixel_depth; + + for ( ; height>0 ; height--) + { + memcpy (dest, src, width * pixel_depth); + src += screens[srcscrn].byte_pitch; + dest += screens[destscrn].byte_pitch; + } +} + +#define FILL_FLAT(dest_type, dest_pitch, pal_func)\ +{\ + const byte *src, *src_p;\ + dest_type *dest, *dest_p;\ + for (sy = y ; sy < y + height; sy += 64)\ + {\ + h = (y + height - sy < 64 ? y + height - sy : 64);\ + dest = (dest_type *)screens[scrn].data + dest_pitch * sy + x;\ + src = data + 64 * ((sy - y) % 64);\ + for (sx = x; sx < x + width; sx += 64)\ + {\ + src_p = src;\ + dest_p = dest;\ + w = (x + width - sx < 64 ? x + width - sx : 64);\ + for (j = 0; j < h; j++)\ + {\ + for (i = 0; i < w; i++)\ + {\ + dest_p[i] = pal_func(src_p[i], VID_COLORWEIGHTMASK);\ + }\ + dest_p += dest_pitch;\ + src_p += 64;\ + }\ + dest += 64;\ + }\ + }\ +}\ + +static void FUNC_V_FillFlat(int lump, int scrn, int x, int y, int width, int height, enum patch_translation_e flags) +{ + /* erase the entire screen to a tiled background */ + const byte *data; + int sx, sy, w, h; + int i, j, pitch; + + lump += firstflat; + + // killough 4/17/98: + data = W_CacheLumpNum(lump); + + if (V_GetMode() == VID_MODE8) { + const byte *src, *src_p; + byte *dest, *dest_p; + pitch = screens[scrn].byte_pitch; + + for (sy = y ; sy < y + height; sy += 64) + { + h = (y + height - sy < 64 ? y + height - sy : 64); + dest = screens[scrn].data + pitch * sy + x; + src = data + 64 * ((sy - y) % 64); + for (sx = x; sx < x + width; sx += 64) + { + src_p = src; + dest_p = dest; + w = (x + width - sx < 64 ? x + width - sx : 64); + for (j = 0; j < h; j++) + { + memcpy (dest_p, src_p, w); + dest_p += pitch; + src_p += 64; + } + dest += 64; + } + } + } else if (V_GetMode() == VID_MODE15) { + pitch = screens[scrn].short_pitch; + FILL_FLAT(unsigned short, pitch, VID_PAL15); + } else if (V_GetMode() == VID_MODE16) { + pitch = screens[scrn].short_pitch; + FILL_FLAT(unsigned short, pitch, VID_PAL16); + } else if (V_GetMode() == VID_MODE32) { + pitch = screens[scrn].int_pitch; + FILL_FLAT(unsigned int, pitch, VID_PAL32); + } + + W_UnlockLumpNum(lump); +} + +static void FUNC_V_FillPatch(int lump, int scrn, int x, int y, int width, int height, enum patch_translation_e flags) +{ + int sx, sy, w, h; + + w = R_NumPatchWidth(lump); + h = R_NumPatchHeight(lump); + + for (sy = y; sy < y + height; sy += h) + { + for (sx = x; sx < x + width; sx += w) + { + V_DrawNumPatch(sx, sy, scrn, lump, CR_DEFAULT, flags); + } + } +} + +/* + * V_DrawBackground tiles a 64x64 patch over the entire screen, providing the + * background for the Help and Setup screens, and plot text betwen levels. + * cphipps - used to have M_DrawBackground, but that was used the framebuffer + * directly, so this is my code from the equivalent function in f_finale.c + */ +static void FUNC_V_DrawBackground(const char* flatname, int scrn) +{ + V_FillFlatName(flatname, scrn, 0, 0, SCREENWIDTH, SCREENHEIGHT, VPT_NONE); +} + +// +// V_Init +// +// Allocates the 4 full screen buffers in low DOS memory +// No return +// + +void V_Init (void) +{ + int i; + + // reset the all + for (i = 0; itopoffset; + x -= patch->leftoffset; + } + + // CPhipps - auto-no-stretch if not high-res + if ((flags & VPT_STRETCH_MASK) && SCREEN_320x200) + flags &= ~VPT_STRETCH_MASK; + + // e6y: wide-res + params = &stretch_params[flags & VPT_ALIGN_MASK]; + + // CPhipps - null translation pointer => no translation + if (!trans) + flags &= ~VPT_TRANS; + + // [FG] automatically center wide patches without horizontal offset + if (patch->width > 320 && patch->leftoffset == 0) + x -= (patch->width - 320) / 2; + + if (V_GetMode() == VID_MODE8 && !(flags & VPT_STRETCH_MASK)) { + int col; + byte *desttop = screens[scrn].data+y*screens[scrn].byte_pitch+x*V_GetPixelDepth(); + int w = patch->width; + + if (y<0 || y+patch->height > ((flags & VPT_STRETCH) ? 200 : SCREENHEIGHT)) { + // killough 1/19/98: improved error message: + lprintf(LO_WARN, "V_DrawMemPatch8: Patch (%d,%d)-(%d,%d) exceeds LFB in vertical direction (horizontal is clipped)\n" + "Bad V_DrawMemPatch8 (flags=%u)", x, y, x+patch->width, y+patch->height, flags); + return; + } + + w--; // CPhipps - note: w = width-1 now, speeds up flipping + + for (col=0 ; col<=w ; desttop++, col++, x++) { + int i; + const int colindex = (flags & VPT_FLIP) ? (w - col) : (col); + const rcolumn_t *column = R_GetPatchColumn(patch, colindex); + + if (x < 0) + continue; + if (x >= SCREENWIDTH) + break; + + // step through the posts in a column + for (i=0; inumPosts; i++) { + const rpost_t *post = &column->posts[i]; + // killough 2/21/98: Unrolled and performance-tuned + + const byte *source = column->pixels + post->topdelta; + byte *dest = desttop + post->topdelta*screens[scrn].byte_pitch; + int count = post->length; + + if (!(flags & VPT_TRANS)) { + if ((count-=4)>=0) + do { + register byte s0,s1; + s0 = source[0]; + s1 = source[1]; + dest[0] = s0; + dest[screens[scrn].byte_pitch] = s1; + dest += screens[scrn].byte_pitch*2; + s0 = source[2]; + s1 = source[3]; + source += 4; + dest[0] = s0; + dest[screens[scrn].byte_pitch] = s1; + dest += screens[scrn].byte_pitch*2; + } while ((count-=4)>=0); + if (count+=4) + do { + *dest = *source++; + dest += screens[scrn].byte_pitch; + } while (--count); + } else { + // CPhipps - merged translation code here + if ((count-=4)>=0) + do { + register byte s0,s1; + s0 = source[0]; + s1 = source[1]; + s0 = trans[s0]; + s1 = trans[s1]; + dest[0] = s0; + dest[screens[scrn].byte_pitch] = s1; + dest += screens[scrn].byte_pitch*2; + s0 = source[2]; + s1 = source[3]; + s0 = trans[s0]; + s1 = trans[s1]; + source += 4; + dest[0] = s0; + dest[screens[scrn].byte_pitch] = s1; + dest += screens[scrn].byte_pitch*2; + } while ((count-=4)>=0); + if (count+=4) + do { + *dest = trans[*source++]; + dest += screens[scrn].byte_pitch; + } while (--count); + } + } + } + } + else { + // CPhipps - move stretched patch drawing code here + // - reformat initialisers, move variables into inner blocks + + int col; + int w = (patch->width << 16) - 1; // CPhipps - -1 for faster flipping + int left, right, top, bottom; + int DXI, DYI; + R_DrawColumn_f colfunc; + draw_column_vars_t dcvars; + draw_vars_t olddrawvars = drawvars; + + R_SetDefaultDrawColumnVars(&dcvars); + + drawvars.byte_topleft = screens[scrn].data; + drawvars.short_topleft = (unsigned short *)screens[scrn].data; + drawvars.int_topleft = (unsigned int *)screens[scrn].data; + drawvars.byte_pitch = screens[scrn].byte_pitch; + drawvars.short_pitch = screens[scrn].short_pitch; + drawvars.int_pitch = screens[scrn].int_pitch; + + if (flags & VPT_TRANS) { + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_TRANSLATED, drawvars.filterpatch, RDRAW_FILTER_NONE); + dcvars.translation = trans; + } else { + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_STANDARD, drawvars.filterpatch, RDRAW_FILTER_NONE); + } + + //e6y: predefined arrays are used + if (!(flags & VPT_STRETCH_MASK)) + { + DXI = 1 << 16; + DYI = 1 << 16; + + left = x; + top = y; + right = x + patch->width - 1; + bottom = y + patch->height; + } + else + { + DXI = params->video->xstep; + DYI = params->video->ystep; + + //FIXME: Is it needed only for F_BunnyScroll? + + left = (x < 0 || x > 320 ? (x * params->video->width) / 320 : params->video->x1lookup[x]); + top = (y < 0 || y > 200 ? (y * params->video->height) / 200 : params->video->y1lookup[y]); + + if (x + patch->width < 0 || x + patch->width > 320) + right = ( ((x + patch->width) * params->video->width - 1) / 320 ); + else + right = params->video->x2lookup[x + patch->width - 1]; + + if (y + patch->height < 0 || y + patch->height > 200) + bottom = ( ((y + patch->height - 0) * params->video->height) / 200 ); + else + bottom = params->video->y2lookup[y + patch->height - 1]; + + left += params->deltax1; + right += params->deltax2; + top += params->deltay1; + bottom += params->deltay1; + } + + dcvars.texheight = patch->height; + dcvars.iscale = DYI; + dcvars.drawingmasked = MAX(patch->width, patch->height) > 8; + dcvars.edgetype = drawvars.patch_edges; + + if (drawvars.filterpatch == RDRAW_FILTER_LINEAR) { + // bias the texture u coordinate + if (patch->flags&PATCH_ISNOTTILEABLE) + col = -(FRACUNIT>>1); + else + col = (patch->width<>1); + } + else { + col = 0; + } + + for (dcvars.x=left; dcvars.x<=right; dcvars.x++, col+=DXI) { + int i; + const int colindex = (flags & VPT_FLIP) ? ((w - col)>>16): (col>>16); + const rcolumn_t *column = R_GetPatchColumn(patch, colindex); + const rcolumn_t *prevcolumn = R_GetPatchColumn(patch, colindex-1); + const rcolumn_t *nextcolumn = R_GetPatchColumn(patch, colindex+1); + + // ignore this column if it's to the left of our clampRect + if (dcvars.x < 0) + continue; + if (dcvars.x >= SCREENWIDTH) + break; + + dcvars.texu = ((flags & VPT_FLIP) ? ((patch->width<width<numPosts; i++) { + const rpost_t *post = &column->posts[i]; + int yoffset = 0; + + //e6y + if (!(flags & VPT_STRETCH_MASK)) + { + dcvars.yl = y + post->topdelta; + dcvars.yh = ((((y + post->topdelta + post->length) << 16) - (FRACUNIT>>1))>>FRACBITS); + } + else + { + // e6y + // More accurate patch drawing from Eternity. + // Predefined arrays are used instead of dynamic calculation + // of the top and bottom screen coordinates of a column. + + int tmpy; + + tmpy = y + post->topdelta; + if (tmpy < 0 || tmpy > 200) + dcvars.yl = (tmpy * params->video->height) / 200 + params->deltay1; + else + dcvars.yl = params->video->y1lookup[tmpy] + params->deltay1; + + tmpy = y + post->topdelta + post->length - 1; + if (tmpy < 0 || tmpy > 200) + dcvars.yh = (tmpy * params->video->height) / 200 + params->deltay1; + else + dcvars.yh = params->video->y2lookup[tmpy] + params->deltay1; + } + dcvars.edgeslope = post->slope; + + if ((dcvars.yh < 0) || (dcvars.yh < top)) + continue; + if ((dcvars.yl >= SCREENHEIGHT) || (dcvars.yl >= bottom)) + continue; + + if (dcvars.yh >= bottom) { + //dcvars.yh = bottom-1; + dcvars.edgeslope &= ~RDRAW_EDGESLOPE_BOT_MASK; + } + if (dcvars.yh >= SCREENHEIGHT) { + dcvars.yh = SCREENHEIGHT-1; + dcvars.edgeslope &= ~RDRAW_EDGESLOPE_BOT_MASK; + } + + if (dcvars.yl < 0) { + yoffset = 0-dcvars.yl; + dcvars.yl = 0; + dcvars.edgeslope &= ~RDRAW_EDGESLOPE_TOP_MASK; + } + if (dcvars.yl < top) { + yoffset = top-dcvars.yl; + dcvars.yl = top; + dcvars.edgeslope &= ~RDRAW_EDGESLOPE_TOP_MASK; + } + + dcvars.source = column->pixels + post->topdelta + yoffset; + dcvars.prevsource = prevcolumn ? prevcolumn->pixels + post->topdelta + yoffset: dcvars.source; + dcvars.nextsource = nextcolumn ? nextcolumn->pixels + post->topdelta + yoffset: dcvars.source; + + dcvars.texturemid = -((dcvars.yl-centery)*dcvars.iscale); + + //e6y + dcvars.dy = params->deltay1; + dcvars.flags |= DRAW_COLUMN_ISPATCH; + + colfunc(&dcvars); + } + } + + R_ResetColumnBuffer(); + drawvars = olddrawvars; + } +} + +// CPhipps - some simple, useful wrappers for that function, for drawing patches from wads + +// CPhipps - GNU C only suppresses generating a copy of a function if it is +// static inline; other compilers have different behaviour. +// This inline is _only_ for the function below + +static void FUNC_V_DrawNumPatch(int x, int y, int scrn, int lump, + int cm, enum patch_translation_e flags) +{ + V_DrawMemPatch(x, y, scrn, R_CachePatchNum(lump), cm, flags); + R_UnlockPatchNum(lump); +} + +static void FUNC_V_DrawNumPatchPrecise(float x, float y, int scrn, int lump, + int cm, enum patch_translation_e flags) +{ + V_DrawMemPatch((int)x, (int)y, scrn, R_CachePatchNum(lump), cm, flags); + R_UnlockPatchNum(lump); +} + +unsigned short *V_Palette15 = NULL; +unsigned short *V_Palette16 = NULL; +unsigned int *V_Palette32 = NULL; +static unsigned short *Palettes15 = NULL; +static unsigned short *Palettes16 = NULL; +static unsigned int *Palettes32 = NULL; +static int currentPaletteIndex = 0; + +// +// V_UpdateTrueColorPalette +// +void V_UpdateTrueColorPalette(video_mode_t mode) { + int i, w, p; + byte r,g,b; + int nr,ng,nb; + float t; + int paletteNum = (V_GetMode() == VID_MODEGL ? 0 : currentPaletteIndex); + static int usegammaOnLastPaletteGeneration = -1; + + int pplump = W_GetNumForName("PLAYPAL"); + int gtlump = (W_CheckNumForName)("GAMMATBL",ns_prboom); + const byte *pal = W_CacheLumpNum(pplump); + // opengl doesn't use the gamma + const byte *const gtable = + (const byte *)W_CacheLumpNum(gtlump) + + (V_GetMode() == VID_MODEGL ? 0 : 256*(usegamma)) + ; + + int numPals = W_LumpLength(pplump) / (3*256); + const float dontRoundAbove = 220; + float roundUpR, roundUpG, roundUpB; + + if (usegammaOnLastPaletteGeneration != usegamma) { + if (Palettes15) free(Palettes15); + if (Palettes16) free(Palettes16); + if (Palettes32) free(Palettes32); + Palettes15 = NULL; + Palettes16 = NULL; + Palettes32 = NULL; + usegammaOnLastPaletteGeneration = usegamma; + } + + if (mode == VID_MODE32) { + if (!Palettes32) { + // set int palette + Palettes32 = malloc(numPals*256*sizeof(int)*VID_NUMCOLORWEIGHTS); + for (p=0; p dontRoundAbove) ? 0 : 0.5f; + roundUpG = (g > dontRoundAbove) ? 0 : 0.5f; + roundUpB = (b > dontRoundAbove) ? 0 : 0.5f; + + for (w=0; w dontRoundAbove) ? 0 : 0.5f; + roundUpG = (g > dontRoundAbove) ? 0 : 0.5f; + roundUpB = (b > dontRoundAbove) ? 0 : 0.5f; + + for (w=0; w>3)*t+roundUpR); + ng = (int)((g>>2)*t+roundUpG); + nb = (int)((b>>3)*t+roundUpB); + Palettes16[((p*256+i)*VID_NUMCOLORWEIGHTS)+w] = ( + (nr<<11) | (ng<<5) | nb + ); + } + } + } + } + V_Palette16 = Palettes16 + paletteNum*256*VID_NUMCOLORWEIGHTS; + } + else if (mode == VID_MODE15) { + if (!Palettes15) { + // set short palette + Palettes15 = malloc(numPals*256*sizeof(short)*VID_NUMCOLORWEIGHTS); + for (p=0; p dontRoundAbove) ? 0 : 0.5f; + roundUpG = (g > dontRoundAbove) ? 0 : 0.5f; + roundUpB = (b > dontRoundAbove) ? 0 : 0.5f; + + for (w=0; w>3)*t+roundUpR); + ng = (int)((g>>3)*t+roundUpG); + nb = (int)((b>>3)*t+roundUpB); + Palettes15[((p*256+i)*VID_NUMCOLORWEIGHTS)+w] = ( + (nr<<10) | (ng<<5) | nb + ); + } + } + } + } + V_Palette15 = Palettes15 + paletteNum*256*VID_NUMCOLORWEIGHTS; + } + + W_UnlockLumpNum(pplump); + W_UnlockLumpNum(gtlump); +} + + +//--------------------------------------------------------------------------- +// V_DestroyTrueColorPalette +//--------------------------------------------------------------------------- +static void V_DestroyTrueColorPalette(video_mode_t mode) { + if (mode == VID_MODE15) { + if (Palettes15) free(Palettes15); + Palettes15 = NULL; + V_Palette15 = NULL; + } + if (mode == VID_MODE16) { + if (Palettes16) free(Palettes16); + Palettes16 = NULL; + V_Palette16 = NULL; + } + if (mode == VID_MODE32) { + if (Palettes32) free(Palettes32); + Palettes32 = NULL; + V_Palette32 = NULL; + } +} + +void V_DestroyUnusedTrueColorPalettes(void) { + if (V_GetMode() != VID_MODE15) V_DestroyTrueColorPalette(VID_MODE15); + if (V_GetMode() != VID_MODE16) V_DestroyTrueColorPalette(VID_MODE16); + if (V_GetMode() != VID_MODE32) V_DestroyTrueColorPalette(VID_MODE32); +} + +// +// V_SetPalette +// +// CPhipps - New function to set the palette to palette number pal. +// Handles loading of PLAYPAL and calls I_SetPalette + +void V_SetPalette(int pal) +{ + currentPaletteIndex = pal; + + if (V_GetMode() == VID_MODEGL) { +#ifdef GL_DOOM + gld_SetPalette(pal); +#endif + } else { + I_SetPalette(pal); + if (V_GetMode() == VID_MODE15 || V_GetMode() == VID_MODE16 || V_GetMode() == VID_MODE32) { + // V_SetPalette can be called as part of the gamma setting before + // we've loaded any wads, which prevents us from reading the palette - POPE + if (W_CheckNumForName("PLAYPAL") >= 0) { + V_UpdateTrueColorPalette(V_GetMode()); + } + } + } +} + +// +// V_FillRect +// +// CPhipps - New function to fill a rectangle with a given colour +static void V_FillRect8(int scrn, int x, int y, int width, int height, byte colour) +{ + byte* dest = screens[scrn].data + x + y*screens[scrn].byte_pitch; + while (height--) { + memset(dest, colour, width); + dest += screens[scrn].byte_pitch; + } +} + +static void V_FillRect15(int scrn, int x, int y, int width, int height, byte colour) +{ + unsigned short* dest = (unsigned short *)screens[scrn].data + x + y*screens[scrn].short_pitch; + int w; + short c = VID_PAL15(colour, VID_COLORWEIGHTMASK); + while (height--) { + for (w=0; wa.fx, fl->a.fy, fl->b.fx, fl->b.fy, color); +} +#endif + +static void NULL_FillRect(int scrn, int x, int y, int width, int height, byte colour) {} +static void NULL_CopyRect(int srcscrn, int destscrn, int x, int y, int width, int height, enum patch_translation_e flags) {} +static void NULL_FillFlat(int lump, int n, int x, int y, int width, int height, enum patch_translation_e flags) {} +static void NULL_FillPatch(int lump, int n, int x, int y, int width, int height, enum patch_translation_e flags) {} +static void NULL_DrawBackground(const char *flatname, int n) {} +static void NULL_DrawNumPatch(int x, int y, int scrn, int lump, int cm, enum patch_translation_e flags) {} +static void NULL_DrawNumPatchPrecise(float x, float y, int scrn, int lump, int cm, enum patch_translation_e flags) {} +static void NULL_DrawBlock(int x, int y, int scrn, int width, int height, const byte *src, enum patch_translation_e flags) {} +static void NULL_PlotPixel(int scrn, int x, int y, byte color) {} +static void NULL_PlotPixelWu(int scrn, int x, int y, byte color, int weight) {} +static void NULL_DrawLine(fline_t* fl, int color) {} +static void NULL_DrawLineWu(fline_t* fl, int color) {} + +const char *default_videomode; +static video_mode_t current_videomode = VID_MODE8; + +V_CopyRect_f V_CopyRect = NULL_CopyRect; +V_FillRect_f V_FillRect = NULL_FillRect; +V_DrawNumPatch_f V_DrawNumPatch = NULL_DrawNumPatch; +V_DrawNumPatchPrecise_f V_DrawNumPatchPrecise = NULL_DrawNumPatchPrecise; +V_FillFlat_f V_FillFlat = NULL_FillFlat; +V_FillPatch_f V_FillPatch = NULL_FillPatch; +V_DrawBackground_f V_DrawBackground = NULL_DrawBackground; +V_PlotPixel_f V_PlotPixel = NULL_PlotPixel; +V_PlotPixelWu_f V_PlotPixelWu = NULL_PlotPixelWu; +V_DrawLine_f V_DrawLine = NULL_DrawLine; +V_DrawLineWu_f V_DrawLineWu = NULL_DrawLineWu; + +// +// V_InitMode +// +void V_InitMode(video_mode_t mode) { + switch (mode) { + case VID_MODE8: + lprintf(LO_INFO, "V_InitMode: using 8 bit video mode\n"); + V_CopyRect = FUNC_V_CopyRect; + V_FillRect = V_FillRect8; + V_DrawNumPatch = FUNC_V_DrawNumPatch; + V_DrawNumPatchPrecise = FUNC_V_DrawNumPatchPrecise; + V_FillFlat = FUNC_V_FillFlat; + V_FillPatch = FUNC_V_FillPatch; + V_DrawBackground = FUNC_V_DrawBackground; + V_PlotPixel = V_PlotPixel8; + V_PlotPixelWu = V_PlotPixelWu8; + V_DrawLine = WRAP_V_DrawLine; + V_DrawLineWu = WRAP_V_DrawLineWu; + current_videomode = VID_MODE8; + break; + case VID_MODE15: + lprintf(LO_INFO, "V_InitMode: using 15 bit video mode\n"); + V_CopyRect = FUNC_V_CopyRect; + V_FillRect = V_FillRect15; + V_DrawNumPatch = FUNC_V_DrawNumPatch; + V_DrawNumPatchPrecise = FUNC_V_DrawNumPatchPrecise; + V_FillFlat = FUNC_V_FillFlat; + V_FillPatch = FUNC_V_FillPatch; + V_DrawBackground = FUNC_V_DrawBackground; + V_PlotPixel = V_PlotPixel15; + V_PlotPixelWu = V_PlotPixelWu15; + V_DrawLine = WRAP_V_DrawLine; + V_DrawLineWu = WRAP_V_DrawLineWu; + current_videomode = VID_MODE15; + break; + case VID_MODE16: + lprintf(LO_INFO, "V_InitMode: using 16 bit video mode\n"); + V_CopyRect = FUNC_V_CopyRect; + V_FillRect = V_FillRect16; + V_DrawNumPatch = FUNC_V_DrawNumPatch; + V_DrawNumPatchPrecise = FUNC_V_DrawNumPatchPrecise; + V_FillFlat = FUNC_V_FillFlat; + V_FillPatch = FUNC_V_FillPatch; + V_DrawBackground = FUNC_V_DrawBackground; + V_PlotPixel = V_PlotPixel16; + V_PlotPixelWu = V_PlotPixelWu16; + V_DrawLine = WRAP_V_DrawLine; + V_DrawLineWu = WRAP_V_DrawLineWu; + current_videomode = VID_MODE16; + break; + case VID_MODE32: + lprintf(LO_INFO, "V_InitMode: using 32 bit video mode\n"); + V_CopyRect = FUNC_V_CopyRect; + V_FillRect = V_FillRect32; + V_DrawNumPatch = FUNC_V_DrawNumPatch; + V_DrawNumPatchPrecise = FUNC_V_DrawNumPatchPrecise; + V_FillFlat = FUNC_V_FillFlat; + V_FillPatch = FUNC_V_FillPatch; + V_DrawBackground = FUNC_V_DrawBackground; + V_PlotPixel = V_PlotPixel32; + V_PlotPixelWu = V_PlotPixelWu32; + V_DrawLine = WRAP_V_DrawLine; + V_DrawLineWu = WRAP_V_DrawLineWu; + current_videomode = VID_MODE32; + break; +#ifdef GL_DOOM + case VID_MODEGL: + lprintf(LO_INFO, "V_InitMode: using OpenGL video mode\n"); + V_CopyRect = WRAP_gld_CopyRect; + V_FillRect = WRAP_gld_FillRect; + V_DrawNumPatch = WRAP_gld_DrawNumPatch; + V_DrawNumPatchPrecise = WRAP_gld_DrawNumPatchPrecise; + V_FillFlat = WRAP_gld_FillFlat; + V_FillPatch = WRAP_gld_FillPatch; + V_DrawBackground = WRAP_gld_DrawBackground; + V_PlotPixel = V_PlotPixelGL; + V_PlotPixelWu = V_PlotPixelWuGL; + V_DrawLine = WRAP_gld_DrawLine; + V_DrawLineWu = WRAP_gld_DrawLine; + current_videomode = VID_MODEGL; + break; +#endif + } + R_FilterInit(); +} + +// +// V_GetMode +// +video_mode_t V_GetMode(void) { + return current_videomode; +} + +// +// V_GetModePixelDepth +// +int V_GetModePixelDepth(video_mode_t mode) { + switch (mode) { + case VID_MODE8: return 1; + case VID_MODE15: return 2; + case VID_MODE16: return 2; + case VID_MODE32: return 4; + default: return 0; + } +} + +// +// V_GetNumPixelBits +// +int V_GetNumPixelBits(void) { + switch (current_videomode) { + case VID_MODE8: return 8; + case VID_MODE15: return 15; + case VID_MODE16: return 16; + case VID_MODE32: return 32; + default: return 0; + } +} + +// +// V_GetPixelDepth +// +int V_GetPixelDepth(void) { + return V_GetModePixelDepth(current_videomode); +} + +// +// V_AllocScreen +// +void V_AllocScreen(screeninfo_t *scrn) { + if (!scrn->not_on_heap) + if ((scrn->byte_pitch * scrn->height) > 0) + //e6y: Clear the screen to black. + scrn->data = calloc(scrn->byte_pitch*scrn->height, 1); +} + +// +// V_AllocScreens +// +void V_AllocScreens(void) { + int i; + + for (i=0; inot_on_heap) { + free(scrn->data); + scrn->data = NULL; + } +} + +// +// V_FreeScreens +// +void V_FreeScreens(void) { + int i; + + for (i=0; ia.x < 0 || fl->a.x >= SCREENWIDTH + || fl->a.y < 0 || fl->a.y >= SCREENHEIGHT + || fl->b.x < 0 || fl->b.x >= SCREENWIDTH + || fl->b.y < 0 || fl->b.y >= SCREENHEIGHT + ) + { + //jff 8/3/98 use logical output routine + lprintf(LO_DEBUG, "fuck %d \r", fuck++); + return; + } +#endif + + 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; + } + } +} + +extern SDL_Surface *screen; +#define RGB2COLOR(r, g, b)\ + ((r >> screen->format->Rloss) << screen->format->Rshift) |\ + ((g >> screen->format->Gloss) << screen->format->Gshift) |\ + ((b >> screen->format->Bloss) << screen->format->Bshift)\ + +// Given 65536, we need 2048; 65536 / 2048 == 32 == 2^5 +// Why 2048? ANG90 == 0x40000000 which >> 19 == 0x800 == 2048. +// The trigonometric correction is based on an angle from 0 to 90. +#define wu_fineshift 5 + +#define wu_weightbits 6 + +// Given 64 levels in the Col2RGB8 table, 65536 / 64 == 1024 == 2^10 +#define wu_fixedshift (16-wu_weightbits) + +// +// V_PlotPixelWu +// +// haleyjd 06/13/09: Pixel plotter for Wu line drawing. +// +static void V_PlotPixelWu8(int scrn, int x, int y, byte color, int weight) +{ + unsigned int bg_color = screens[scrn].data[x+screens[scrn].byte_pitch*y]; + unsigned int *fg2rgb = Col2RGB8[weight]; + unsigned int *bg2rgb = Col2RGB8[64 - weight]; + unsigned int fg = fg2rgb[color]; + unsigned int bg = bg2rgb[bg_color]; + + fg = (fg + bg) | 0x1f07c1f; + V_PlotPixel(scrn, x, y, RGB32k[0][0][fg & (fg >> 15)]); +} +static void V_PlotPixelWu15(int scrn, int x, int y, byte color, int weight) +{ + const unsigned char *rgb = V_GetPlaypal() + color * 3; + + byte r = (*(rgb + 0) * weight) >> wu_weightbits; + byte g = (*(rgb + 1) * weight) >> wu_weightbits; + byte b = (*(rgb + 2) * weight) >> wu_weightbits; + + ((unsigned short *)screens[scrn].data)[x+screens[scrn].short_pitch*y] = + (unsigned short)RGB2COLOR(r, g, b); +} +static void V_PlotPixelWu16(int scrn, int x, int y, byte color, int weight) +{ + const unsigned char *rgb = V_GetPlaypal() + color * 3; + + byte r = (*(rgb + 0) * weight) >> wu_weightbits; + byte g = (*(rgb + 1) * weight) >> wu_weightbits; + byte b = (*(rgb + 2) * weight) >> wu_weightbits; + + ((unsigned short *)screens[scrn].data)[x+screens[scrn].short_pitch*y] = + (unsigned short)RGB2COLOR(r, g, b); +} +static void V_PlotPixelWu32(int scrn, int x, int y, byte color, int weight) +{ + const unsigned char *rgb = V_GetPlaypal() + color * 3; + + byte r = (*(rgb + 0) * weight) >> wu_weightbits; + byte g = (*(rgb + 1) * weight) >> wu_weightbits; + byte b = (*(rgb + 2) * weight) >> wu_weightbits; + + ((unsigned int *)screens[scrn].data)[x+screens[scrn].int_pitch*y] = + (unsigned int)RGB2COLOR(r, g, b); +} + +// +// WRAP_V_DrawLineWu +// +// haleyjd 06/12/09: Wu line drawing for the automap, with trigonometric +// brightness correction by SoM. I call this the Wu-McGranahan line drawing +// algorithm. +// +void WRAP_V_DrawLineWu(fline_t *fl, int color) +{ + unsigned short erroracc, erroradj, erroracctmp; + int dx, dy, xdir = 1; + int x, y; + + // swap end points if necessary + if(fl->a.y > fl->b.y) + { + fpoint_t tmp = fl->a; + + fl->a = fl->b; + fl->b = tmp; + } + + // determine change in x, y and direction of travel + dx = fl->b.x - fl->a.x; + dy = fl->b.y - fl->a.y; + + if(dx < 0) + { + dx = -dx; + xdir = -xdir; + } + + // detect special cases -- horizontal, vertical, and 45 degrees; + // revert to Bresenham + if(dx == 0 || dy == 0 || dx == dy) + { + V_DrawLine(fl, color); + return; + } + + // draw first pixel + PUTDOT(fl->a.x, fl->a.y, color); + + x = fl->a.x; + y = fl->a.y; + + if(dy > dx) + { + // line is y-axis major. + erroracc = 0; + erroradj = (unsigned short)(((unsigned int)dx << 16) / (unsigned int)dy); + + while(--dy) + { + erroracctmp = erroracc; + + erroracc += erroradj; + + // if error has overflown, advance x coordinate + if(erroracc <= erroracctmp) + x += xdir; + + y += 1; // advance y + + // the trick is in the trig! + V_PlotPixelWu(0, x, y, (byte)color, + finecosine[erroracc >> wu_fineshift] >> wu_fixedshift); + V_PlotPixelWu(0, x + xdir, y, (byte)color, + finesine[erroracc >> wu_fineshift] >> wu_fixedshift); + } + } + else + { + // line is x-axis major. + erroracc = 0; + erroradj = (unsigned short)(((unsigned int)dy << 16) / (unsigned int)dx); + + while(--dx) + { + erroracctmp = erroracc; + + erroracc += erroradj; + + // if error has overflown, advance y coordinate + if(erroracc <= erroracctmp) + y += 1; + + x += xdir; // advance x + + // the trick is in the trig! + V_PlotPixelWu(0, x, y, (byte)color, + finecosine[erroracc >> wu_fineshift] >> wu_fixedshift); + V_PlotPixelWu(0, x, y + 1, (byte)color, + finesine[erroracc >> wu_fineshift] >> wu_fixedshift); + } + } + + // draw last pixel + PUTDOT(fl->b.x, fl->b.y, color); +} + + +static unsigned char *playpal_data = NULL; +const unsigned char* V_GetPlaypal(void) +{ + if (!playpal_data) + { + int lump = W_GetNumForName("PLAYPAL"); + int len = W_LumpLength(lump); + const byte *data = W_CacheLumpNum(lump); + playpal_data = malloc(len); + memcpy(playpal_data, data, len); + W_UnlockLumpNum(lump); + } + + return playpal_data; +} + +void V_FreePlaypal(void) +{ + if (playpal_data) + { + free(playpal_data); + playpal_data = NULL; + } +} + +void V_FillBorder(int lump, byte color) +{ + int bordtop, bordbottom, bordleft, bordright; + + if (render_stretch_hud == patch_stretch_full) + return; + + bordleft = wide_offsetx; + bordright = wide_offset2x - wide_offsetx; + bordtop = wide_offsety; + bordbottom = wide_offset2y - wide_offsety; + + if (lump >= 0) + { + if (bordtop > 0) + { + // Top + V_FillFlat(lump, 0, 0, 0, SCREENWIDTH, bordtop, VPT_NONE); + // Bottom + V_FillFlat(lump, 0, 0, SCREENHEIGHT - bordbottom, SCREENWIDTH, bordbottom, VPT_NONE); + } + if (bordleft > 0) + { + // Left + V_FillFlat(lump, 0, 0, bordtop, bordleft, SCREENHEIGHT - bordbottom - bordtop, VPT_NONE); + // Right + V_FillFlat(lump, 0, SCREENWIDTH - bordright, bordtop, bordright, SCREENHEIGHT - bordbottom - bordtop, VPT_NONE); + } + } + else + { + if (bordtop > 0) + { + // Top + V_FillRect(0, 0, 0, SCREENWIDTH, bordtop, color); + // Bottom + V_FillRect(0, 0, SCREENHEIGHT - bordbottom, SCREENWIDTH, bordbottom, color); + } + if (bordleft > 0) + { + // Left + V_FillRect(0, 0, bordtop, bordleft, SCREENHEIGHT - bordbottom - bordtop, color); + // Right + V_FillRect(0, SCREENWIDTH - bordright, bordtop, bordright, SCREENHEIGHT - bordbottom - bordtop, color); + } + } +} + +// DWF 2012-05-10 +// Euclid's algorithm to find the greatest common divisor. +static unsigned int gcd (unsigned int n, unsigned int d) { return (d ? gcd(d, n%d) : n); } + +// DWF 2012-05-10 +// Reduce aspect ratio fractions to make the log messages nicer. Even if +// integer math were still being used for FPS scaling, this would not +// necessarily speed it up, but it does no harm. +// Order of parameters (numerator, denominator) doesn't matter. +static void ReduceFraction (unsigned *num1, unsigned *num2) +{ + unsigned int g; + assert(*num1 || *num2); + g = gcd (*num1, *num2); + *num1 /= g; + *num2 /= g; +} + +// DWF 2012-05-01 +// C substitute for C++ std::swap. +static void swap(unsigned int *num1, unsigned int *num2) +{ + unsigned int temp = *num1; + *num1 = *num2; + *num2 = temp; +} + +// DWF 2012-05-01 +// Set global variables for video scaling. +void SetRatio(int width, int height) +{ + lprintf(LO_INFO, "SetRatio: width/height parameters %dx%d\n", width, height); + + ratio_multiplier = width; + ratio_scale = height; + ReduceFraction(&ratio_multiplier, &ratio_scale); + + // The terms storage aspect ratio, pixel aspect ratio, and display aspect + // ratio came from Wikipedia. SAR x PAR = DAR + lprintf(LO_INFO, "SetRatio: storage aspect ratio %u:%u\n", ratio_multiplier, ratio_scale); + if (height == 200 || height == 400) + { + lprintf(LO_INFO, "SetRatio: recognized VGA mode with pixel aspect ratio 5:6\n"); + ratio_multiplier = width * 5; + ratio_scale = height * 6; + ReduceFraction(&ratio_multiplier, &ratio_scale); + } + else + { + lprintf(LO_INFO, "SetRatio: assuming square pixels\n"); + } + lprintf(LO_INFO, "SetRatio: display aspect ratio %u:%u\n", ratio_multiplier, ratio_scale); + + // If user wants to force aspect ratio, let them. + { + unsigned int new_multiplier = ratio_multiplier; + unsigned int new_scale = ratio_scale; + // Hardcoded to match render_aspects_list + switch (render_aspect) + { + case 0: + break; + case 1: + new_multiplier = 16; + new_scale = 9; + break; + case 2: + new_multiplier = 16; + new_scale = 10; + break; + case 3: + new_multiplier = 4; + new_scale = 3; + break; + case 4: + new_multiplier = 5; + new_scale = 4; + break; + default: + lprintf(LO_ERROR, "SetRatio: render_aspect has invalid value %d\n", render_aspect); + } + + if (ratio_multiplier != new_multiplier || ratio_scale != new_scale) + { + lprintf(LO_INFO, "SetRatio: overruled by user configuration setting\n"); + ratio_multiplier = new_multiplier; + ratio_scale = new_scale; + lprintf(LO_INFO, "SetRatio: revised display aspect ratio %u:%u\n", ratio_multiplier, ratio_scale); + } + } + + gl_ratio = RMUL * ratio_multiplier / ratio_scale; + lprintf(LO_INFO, "SetRatio: gl_ratio %f\n", gl_ratio); + + // Calculate modified multiplier following the pattern of the old + // BaseRatioSizes table in PrBoom-Plus 2.5.1.3. + swap(&ratio_multiplier, &ratio_scale); + ratio_multiplier *= 4; + ratio_scale *= 3; + ReduceFraction(&ratio_multiplier, &ratio_scale); + + tallscreen = (ratio_scale < ratio_multiplier); + if (tallscreen) + { + lprintf(LO_INFO, "SetRatio: tallscreen aspect recognized; flipping multiplier\n"); + swap(&ratio_multiplier, &ratio_scale); + psprite_offset = (int)(6.5*FRACUNIT); + } + else + { + psprite_offset = 0; + } + lprintf(LO_INFO, "SetRatio: multiplier %u/%u\n", ratio_multiplier, ratio_scale); + + // The rest is carried over from CheckRatio in PrBoom-Plus 2.5.1.3. + if (tallscreen) + { + WIDE_SCREENWIDTH = SCREENWIDTH; + WIDE_SCREENHEIGHT = SCREENHEIGHT * ratio_multiplier / ratio_scale; + } + else + { + WIDE_SCREENWIDTH = SCREENWIDTH * ratio_multiplier / ratio_scale; + WIDE_SCREENHEIGHT = SCREENHEIGHT; + } + + WIDE_SCREENWIDTH = MAX(1, WIDE_SCREENWIDTH); + WIDE_SCREENHEIGHT = MAX(1, WIDE_SCREENHEIGHT); + + yaspectmul = Scale((320< 0) + { + patches_scalex = MIN(render_patches_scalex, patches_scalex); + } + if (render_patches_scaley > 0) + { + patches_scaley = MIN(render_patches_scaley, patches_scaley); + } + + ST_SCALED_HEIGHT = ST_HEIGHT * patches_scaley; + + if (SCREENWIDTH < 320 || WIDE_SCREENWIDTH < 320 || + SCREENHEIGHT < 200 || WIDE_SCREENHEIGHT < 200) + { + render_stretch_hud = patch_stretch_full; + } + + switch (render_stretch_hud) + { + case patch_stretch_16x10: + ST_SCALED_Y = (200 * patches_scaley - ST_SCALED_HEIGHT); + + wide_offset2x = (SCREENWIDTH - patches_scalex * 320); + wide_offset2y = (SCREENHEIGHT - patches_scaley * 200); + break; + case patch_stretch_4x3: + ST_SCALED_HEIGHT = ST_HEIGHT * WIDE_SCREENHEIGHT / 200; + + ST_SCALED_Y = SCREENHEIGHT - ST_SCALED_HEIGHT; + + wide_offset2x = (SCREENWIDTH - WIDE_SCREENWIDTH); + wide_offset2y = (SCREENHEIGHT - WIDE_SCREENHEIGHT); + break; + case patch_stretch_full: + ST_SCALED_HEIGHT = ST_HEIGHT * SCREENHEIGHT / 200; + + ST_SCALED_Y = SCREENHEIGHT - ST_SCALED_HEIGHT; + wide_offset2x = 0; + wide_offset2y = 0; + break; + } + + wide_offsetx = wide_offset2x / 2; + wide_offsety = wide_offset2y / 2; + + SCREEN_320x200 = + (SCREENWIDTH == 320) && (SCREENHEIGHT == 200) && + (WIDE_SCREENWIDTH == 320) && (WIDE_SCREENHEIGHT == 200); + + // [FG] support widescreen status bar backgrounds + ST_SetScaledWidth(); +} + +void V_GetWideRect(int *x, int *y, int *w, int *h, enum patch_translation_e flags) +{ + stretch_param_t *params = &stretch_params[flags & VPT_ALIGN_MASK]; + int sx = *x; + int sy = *y; + + *x = params->video->x1lookup[*x]; + *y = params->video->y1lookup[*y]; + *w = params->video->x2lookup[sx + *w - 1] - *x + 1; + *h = params->video->y2lookup[sy + *h - 1] - *y + 1; + *x += params->deltax1; + *y += params->deltay1; +} + +// +// V_BestColor +// +// Adapted from zdoom -- thanks to Randy Heit. +// +// This always assumes a 256-color palette; +// it's intended for use in startup functions to match hard-coded +// color values to the best fit in the game's palette (allows +// cross-game usage among other things). +// +int V_BestColor(const unsigned char *palette, int r, int g, int b) +{ + int color; + + // use color 0 as a worst-case match for any color + int bestcolor = 0; + int bestdist = 257 * 257 + 257 * 257 + 257 * 257; + + for (color = 0; color < 256; color++) + { + int dr, dg, db, dist; + + dr = r - *palette++; + dg = g - *palette++; + db = b - *palette++; + + dist = dr * dr + dg * dg + db * db; + + if (dist < bestdist) + { + // exact match + if (dist == 0) + return color; + + bestdist = dist; + bestcolor = color; + } + } + + return bestcolor; +} + +// Alt-Enter: fullscreen <-> windowed +void V_ToggleFullscreen(void) +{ + if (desired_fullscreen == use_fullscreen) + { + use_fullscreen = (use_fullscreen ? 0 : 1); + desired_fullscreen = use_fullscreen; + } + else + { + desired_fullscreen = (desired_fullscreen ? 0 : 1); + } + + I_UpdateVideoMode(); + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + gld_PreprocessLevel(); + } +#endif +} + +void V_ChangeScreenResolution(void) +{ + I_UpdateVideoMode(); + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + gld_PreprocessLevel(); + } +#endif +} diff --git a/src/v_video.h b/src/v_video.h new file mode 100644 index 0000000..cab63bf --- /dev/null +++ b/src/v_video.h @@ -0,0 +1,314 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Gamma correction LUT. + * Color range translation support + * Functions to draw patches (by post) directly to screen. + * Functions to blit a block to the screen. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __V_VIDEO__ +#define __V_VIDEO__ + +#include "doomtype.h" +#include "doomdef.h" +// Needed because we are refering to patches. +#include "r_data.h" + +// +// VIDEO +// + +typedef enum +{ + patch_stretch_16x10, + patch_stretch_4x3, + patch_stretch_full, + + patch_stretch_max +} patch_stretch_t; + +typedef struct +{ + fixed_t xstep, ystep; + + int width, height; + + // SoM 1-31-04: This will insure that scaled patches and such are put in the right places + short x1lookup[321]; + short y1lookup[201]; + short x2lookup[321]; + short y2lookup[201]; +} cb_video_t; + +typedef struct stretch_param_s +{ + cb_video_t *video; + int deltax1; + int deltay1; + int deltax2; + int deltay2; +} stretch_param_t; + +extern stretch_param_t stretch_params_table[3][VPT_ALIGN_MAX]; +extern stretch_param_t *stretch_params; + +extern cb_video_t video; +extern cb_video_t video_stretch; +extern cb_video_t video_full; +extern int patches_scalex; +extern int patches_scaley; + +extern const char *render_aspects_list[]; +extern const char *render_stretch_list[]; + +extern int render_stretch_hud; +extern int render_stretch_hud_default; +extern int render_patches_scalex; +extern int render_patches_scaley; + +// DWF 2012-05-10 +// SetRatio sets the following global variables based on window geometry and +// user preferences. The integer ratio is hardly used anymore, so further +// simplification may be in order. +void SetRatio(int width, int height); +extern dboolean tallscreen; +extern unsigned int ratio_multiplier, ratio_scale; +extern float gl_ratio; +extern int psprite_offset; // Needed for "tallscreen" modes + +#define CENTERY (SCREENHEIGHT/2) + +// Screen 0 is the screen updated by I_Update screen. +// Screen 1 is an extra buffer. + +// array of pointers to color translation tables +extern const byte *colrngs[]; + +// symbolic indices into color translation table pointer array +typedef enum +{ + CR_BRICK, //0 + CR_TAN, //1 + CR_GRAY, //2 + CR_GREEN, //3 + CR_BROWN, //4 + CR_GOLD, //5 + CR_RED, //6 + CR_BLUE, //7 + CR_ORANGE, //8 + CR_YELLOW, //9 + CR_BLUE2, //10 // proff + CR_BLACK, //11 + CR_PURPLE, //12 + CR_WHITE, //13 + CR_LIMIT //14 //jff 2/27/98 added for range check +} crange_idx_e; +//jff 1/16/98 end palette color range additions + +#define CR_DEFAULT CR_RED /* default value for out of range colors */ + +typedef struct { + byte *data; // pointer to the screen content + dboolean not_on_heap; // if set, no malloc or free is preformed and + // data never set to NULL. Used i.e. with SDL doublebuffer. + int width; // the width of the surface + int height; // the height of the surface, used when mallocing + int byte_pitch; // tha actual width of one line, used when mallocing + int short_pitch; // tha actual width of one line, used when mallocing + int int_pitch; // tha actual width of one line, used when mallocing +} screeninfo_t; + +#define NUM_SCREENS 6 +extern screeninfo_t screens[NUM_SCREENS]; +extern int usegamma; + +// Varying bit-depth support -POPE +// +// For bilinear filtering, each palette color is pre-weighted and put in a +// table for fast blending operations. These macros decide how many weights +// to create for each color. The lower the number, the lower the blend +// accuracy, which can produce very bad artifacts in texture filtering. +#define VID_NUMCOLORWEIGHTS 64 +#define VID_COLORWEIGHTMASK (VID_NUMCOLORWEIGHTS-1) +#define VID_COLORWEIGHTBITS 6 + +// Palettes for converting from 8 bit color to 16 and 32 bit. Also +// contains the weighted versions of each palette color for filtering +// operations +extern unsigned short *V_Palette15; +extern unsigned short *V_Palette16; +extern unsigned int *V_Palette32; + +#define VID_PAL15(color, weight) V_Palette15[ (color)*VID_NUMCOLORWEIGHTS + (weight) ] +#define VID_PAL16(color, weight) V_Palette16[ (color)*VID_NUMCOLORWEIGHTS + (weight) ] +#define VID_PAL32(color, weight) V_Palette32[ (color)*VID_NUMCOLORWEIGHTS + (weight) ] + +// The available bit-depth modes +typedef enum { + VID_MODE8, + VID_MODE15, + VID_MODE16, + VID_MODE32, + VID_MODEGL, + VID_MODEMAX +} video_mode_t; + +extern const char *default_videomode; + +void V_InitMode(video_mode_t mode); + +// video mode query interface +video_mode_t V_GetMode(void); +int V_GetModePixelDepth(video_mode_t mode); +int V_GetNumPixelBits(void); +int V_GetPixelDepth(void); + +//jff 4/24/98 loads color translation lumps +void V_InitColorTranslation(void); + +void V_InitFlexTranTable(void); + +// Allocates buffer screens, call before R_Init. +void V_Init (void); + +// V_CopyRect +typedef void (*V_CopyRect_f)(int srcscrn, int destscrn, + int x, int y, + int width, int height, + enum patch_translation_e flags); +extern V_CopyRect_f V_CopyRect; + +// V_FillRect +typedef void (*V_FillRect_f)(int scrn, int x, int y, + int width, int height, byte colour); +extern V_FillRect_f V_FillRect; + +// CPhipps - patch drawing +// Consolidated into the 3 really useful functions: + +// V_DrawNumPatch - Draws the patch from lump num +typedef void (*V_DrawNumPatch_f)(int x, int y, int scrn, + int lump, int cm, + enum patch_translation_e flags); +extern V_DrawNumPatch_f V_DrawNumPatch; + +typedef void (*V_DrawNumPatchPrecise_f)(float x, float y, int scrn, + int lump, int cm, + enum patch_translation_e flags); +extern V_DrawNumPatchPrecise_f V_DrawNumPatchPrecise; + +// V_DrawNamePatch - Draws the patch from lump "name" +#define V_DrawNamePatch(x,y,s,n,t,f) V_DrawNumPatch(x,y,s,W_GetNumForName(n),t,f) +#define V_DrawNamePatchPrecise(x,y,s,n,t,f) V_DrawNumPatchPrecise(x,y,s,W_GetNumForName(n),t,f) + +/* cph - + * Functions to return width & height of a patch. + * Doesn't really belong here, but is often used in conjunction with + * this code. + */ +#define V_NamePatchWidth(name) R_NumPatchWidth(W_GetNumForName(name)) +#define V_NamePatchHeight(name) R_NumPatchHeight(W_GetNumForName(name)) + +// e6y +typedef void (*V_FillFlat_f)(int lump, int scrn, int x, int y, int width, int height, enum patch_translation_e flags); +extern V_FillFlat_f V_FillFlat; +#define V_FillFlatName(flatname, scrn, x, y, width, height, flags) \ + V_FillFlat(R_FlatNumForName(flatname), (scrn), (x), (y), (width), (height), (flags)) + +typedef void (*V_FillPatch_f)(int lump, int scrn, int x, int y, int width, int height, enum patch_translation_e flags); +extern V_FillPatch_f V_FillPatch; +#define V_FillPatchName(name, scrn, x, y, width, height, flags) \ + V_FillPatch(W_GetNumForName(name), (scrn), (x), (y), (width), (height), (flags)) + + +/* cphipps 10/99: function to tile a flat over the screen */ +typedef void (*V_DrawBackground_f)(const char* flatname, int scrn); +extern V_DrawBackground_f V_DrawBackground; + +void V_DestroyUnusedTrueColorPalettes(void); +// CPhipps - function to set the palette to palette number pal. +void V_SetPalette(int pal); + +// Alt-Enter: fullscreen <-> windowed +void V_ToggleFullscreen(void); +void V_ChangeScreenResolution(void); + +// CPhipps - function to plot a pixel + +// V_PlotPixel +typedef void (*V_PlotPixel_f)(int,int,int,byte); +extern V_PlotPixel_f V_PlotPixel; + +typedef struct +{ + int x, y; + float fx, fy; +} fpoint_t; + +typedef struct +{ + fpoint_t a, b; +} fline_t; + +// V_DrawLine +typedef void (*V_DrawLine_f)(fline_t* fl, int color); +extern V_DrawLine_f V_DrawLine; + +// V_DrawLineWu +typedef void (*V_DrawLineWu_f)(fline_t* fl, int color); +extern V_DrawLineWu_f V_DrawLineWu; + +// V_PlotPixelWu +typedef void (*V_PlotPixelWu_f)(int scrn, int x, int y, byte color, int weight); +extern V_PlotPixelWu_f V_PlotPixelWu; + +void V_AllocScreen(screeninfo_t *scrn); +void V_AllocScreens(); +void V_FreeScreen(screeninfo_t *scrn); +void V_FreeScreens(); + +const unsigned char* V_GetPlaypal(void); +void V_FreePlaypal(void); + +// e6y: wide-res +void V_FillBorder(int lump, byte color); + +void V_GetWideRect(int *x, int *y, int *w, int *h, enum patch_translation_e flags); + +int V_BestColor(const unsigned char *palette, int r, int g, int b); + +// [FG] colored blood and gibs +int V_BloodColor(int blood); + +#ifdef GL_DOOM +#include "gl_struct.h" +#endif +#endif diff --git a/src/version.c b/src/version.c new file mode 100644 index 0000000..1f5401f --- /dev/null +++ b/src/version.c @@ -0,0 +1,47 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Date stamp + * + *----------------------------------------------------------------------------- + */ + + +#include "doomdef.h" +#include "version.h" + +#ifndef BUILD_DATE +#define BUILD_DATE __DATE__ +#endif + +#ifndef BUILD_TIME +#define BUILD_TIME __TIME__ +#endif + +const char version_date[] = BUILD_DATE " " BUILD_TIME; diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..f7ad161 --- /dev/null +++ b/src/version.h @@ -0,0 +1,40 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Doom version indicators. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __DOOMVERSION__ +#define __DOOMVERSION__ + +extern const char version_date[]; + +#endif diff --git a/src/w_memcache.c b/src/w_memcache.c new file mode 100644 index 0000000..2d01bcc --- /dev/null +++ b/src/w_memcache.c @@ -0,0 +1,154 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2001 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Handles in-memory caching of WAD lumps + * + *----------------------------------------------------------------------------- + */ + +// use config.h if autoconf made one -- josh +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomstat.h" +#include "doomtype.h" + +#ifdef __GNUG__ +#pragma implementation "w_wad.h" +#endif +#include "w_wad.h" +#include "z_zone.h" +#include "lprintf.h" + +static struct { + void *cache; +#ifdef TIMEDIAG + int locktic; +#endif + unsigned int locks; +} *cachelump; + +#ifdef HEAPDUMP +void W_PrintLump(FILE* fp, void* p) { + int i; + for (i=0; i= (unsigned)numlumps) + I_Error ("W_CacheLumpNum: %i >= numlumps",lump); +#endif + + if (!cachelump[lump].cache) // read the lump in + W_ReadLump(lump, Z_Malloc(W_LumpLength(lump), PU_CACHE, &cachelump[lump].cache)); + + /* cph - if wasn't locked but now is, tell z_zone to hold it */ + if (!cachelump[lump].locks && locks) { + Z_ChangeTag(cachelump[lump].cache,PU_STATIC); +#ifdef TIMEDIAG + cachelump[lump].locktic = gametic; +#endif + } + cachelump[lump].locks += locks; + + return cachelump[lump].cache; +} + +const void *W_LockLumpNum(int lump) +{ + return W_CacheLumpNum(lump); +} + +/* + * W_UnlockLumpNum + * + * CPhipps - this changes (should reduce) the number of locks on a lump + */ + +void W_UnlockLumpNum(int lump) +{ + const int unlocks = 1; + cachelump[lump].locks -= unlocks; + /* cph - Note: must only tell z_zone to make purgeable if currently locked, + * else it might already have been purged + */ + if (unlocks && !cachelump[lump].locks) + Z_ChangeTag(cachelump[lump].cache, PU_CACHE); +} + diff --git a/src/w_mmap.c b/src/w_mmap.c new file mode 100644 index 0000000..8062521 --- /dev/null +++ b/src/w_mmap.c @@ -0,0 +1,350 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 2001 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Transparent access to data in WADs using mmap + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#include +#endif + +#include "doomstat.h" +#include "doomtype.h" + +#ifdef __GNUG__ +#pragma implementation "w_wad.h" +#endif +#include "w_wad.h" +#include "z_zone.h" +#include "lprintf.h" +#include "i_system.h" + +#include "e6y.h"//e6y + +static struct { + void *cache; +#ifdef TIMEDIAG + int locktic; +#endif + int locks; +} *cachelump; + +#ifdef HEAPDUMP +void W_PrintLump(FILE* fp, void* p) { + int i; + for (i=0; i 0) + { + lprintf(LO_DEBUG, "%8.8s %6u %2d %6d\n", lumpinfo[i].name, + W_LumpLength(i), cachelump[i].locks, gametic - cachelump[i].locktic); + } + } + } +} +#endif + +#ifdef _WIN32 +typedef struct { + HANDLE hnd; + OFSTRUCT fileinfo; + HANDLE hnd_map; + void *data; +} mmap_info_t; + +mmap_info_t *mapped_wad; + +void W_DoneCache(void) +{ + size_t i; + + if (cachelump) { + free(cachelump); + cachelump = NULL; + } + + if (!mapped_wad) + return; + for (i=0; i=numwadfiles)) + I_Error("W_InitCache: wad_index out of range"); +#endif + if (!mapped_wad[wad_index].data) + { + wchar_t *wname = ConvertToUtf8(wadfiles[wad_index].name); + mapped_wad[wad_index].hnd = CreateFileW(wname, + GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + free(wname); + if (mapped_wad[wad_index].hnd==INVALID_HANDLE_VALUE) + I_Error("W_InitCache: CreateFile for memory mapping failed (LastError %i)",GetLastError()); + mapped_wad[wad_index].hnd_map = + CreateFileMapping( + mapped_wad[wad_index].hnd, + NULL, + PAGE_READONLY, + 0, + 0, + NULL + ); + if (mapped_wad[wad_index].hnd_map==NULL) + I_Error("W_InitCache: CreateFileMapping for memory mapping failed (LastError %i)",GetLastError()); + mapped_wad[wad_index].data = + MapViewOfFile( + mapped_wad[wad_index].hnd_map, + FILE_MAP_READ, + 0, + 0, + 0 + ); + if (mapped_wad[wad_index].data==NULL) + I_Error("W_InitCache: MapViewOfFile for memory mapping failed (LastError %i)",GetLastError()); + } + } + } +} + +const void* W_CacheLumpNum(int lump) +{ + int wad_index = (int)(lumpinfo[lump].wadfile-wadfiles); +#ifdef RANGECHECK + if ((wad_index<0)||((size_t)wad_index>=numwadfiles)) + I_Error("W_CacheLumpNum: wad_index out of range"); + if ((unsigned)lump >= (unsigned)numlumps) + I_Error ("W_CacheLumpNum: %i >= numlumps",lump); +#endif + if (!lumpinfo[lump].wadfile) + return NULL; + return (void*)((unsigned char *)mapped_wad[wad_index].data+lumpinfo[lump].position); +} + +#else + +void ** mapped_wad; + +void W_InitCache(void) +{ + int maxfd = 0; + // set up caching + cachelump = calloc(numlumps, sizeof *cachelump); + if (!cachelump) + I_Error ("W_Init: Couldn't allocate lumpcache"); + +#ifdef TIMEDIAG + I_AtExit(W_ReportLocks, true); +#endif + + { + int i; + for (i=0; ihandle > maxfd) maxfd = lumpinfo[i].wadfile->handle; + } + mapped_wad = calloc(maxfd+1,sizeof *mapped_wad); + { + int i; + for (i=0; ihandle; + if (!mapped_wad[fd]) + if ((mapped_wad[fd] = mmap(NULL,I_Filelength(fd),PROT_READ,MAP_SHARED,fd,0)) == MAP_FAILED) + I_Error("W_InitCache: failed to mmap"); + } + } + } +} + +void W_DoneCache(void) +{ + { + int i; + for (i=0; ihandle; + if (mapped_wad[fd]) { + if (munmap(mapped_wad[fd],I_Filelength(fd))) + I_Error("W_DoneCache: failed to munmap"); + mapped_wad[fd] = NULL; + } + } + } + free(mapped_wad); + mapped_wad = NULL; +} + +const void* W_CacheLumpNum(int lump) +{ +#ifdef RANGECHECK + if ((unsigned)lump >= (unsigned)numlumps) + I_Error ("W_CacheLumpNum: %i >= numlumps",lump); +#endif + if (!lumpinfo[lump].wadfile) + return NULL; + + return + (const void *) ( + ((const byte *) (mapped_wad[lumpinfo[lump].wadfile->handle])) + + lumpinfo[lump].position + ); +} +#endif + +/* + * W_LockLumpNum + * + * This copies the lump into a malloced memory region and returns its address + * instead of returning a pointer into the memory mapped area + * + */ +const void* W_LockLumpNum(int lump) +{ + size_t len = W_LumpLength(lump); + const void *data = W_CacheLumpNum(lump); + + if (!cachelump[lump].cache) { + // read the lump in + Z_Malloc(len, PU_CACHE, &cachelump[lump].cache); + memcpy(cachelump[lump].cache, data, len); + } + + /* cph - if wasn't locked but now is, tell z_zone to hold it */ + if (cachelump[lump].locks <= 0) { + Z_ChangeTag(cachelump[lump].cache,PU_STATIC); +#ifdef TIMEDIAG + cachelump[lump].locktic = gametic; +#endif + // reset lock counter + cachelump[lump].locks = 1; + } else { + // increment lock counter + cachelump[lump].locks += 1; + } + + return cachelump[lump].cache; +} + +void W_UnlockLumpNum(int lump) { + if (cachelump[lump].locks == -1) + return; // this lump is memory mapped + + cachelump[lump].locks -= 1; + /* cph - Note: must only tell z_zone to make purgeable if currently locked, + * else it might already have been purged + */ + if (cachelump[lump].locks == 0) + Z_ChangeTag(cachelump[lump].cache, PU_CACHE); +} + diff --git a/src/w_wad.c b/src/w_wad.c new file mode 100644 index 0000000..dce1bcd --- /dev/null +++ b/src/w_wad.c @@ -0,0 +1,604 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2001 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Handles WAD file header, directory, lump I/O. + * + *----------------------------------------------------------------------------- + */ + +// use config.h if autoconf made one -- josh +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef _MSC_VER +#include +#include +#endif +#include + +#include "doomstat.h" +#include "d_net.h" +#include "doomtype.h" +#include "i_system.h" +#include "r_main.h" + +#ifdef __GNUG__ +#pragma implementation "w_wad.h" +#endif +#include "w_wad.h" +#include "lprintf.h" + +//e6y +#include "r_demo.h" +#include "e6y.h" + +#include "m_io.h" + +// +// GLOBALS +// + +// Location of each lump on disk. +lumpinfo_t *lumpinfo; +int numlumps; // killough + +void ExtractFileBase (const char *path, char *dest) +{ + const char *src = path + strlen(path) - 1; + int length; + + // back up until a \ or the start + while (src != path && src[-1] != ':' // killough 3/22/98: allow c:filename + && *(src-1) != '\\' + && *(src-1) != '/') + { + src--; + } + + // copy up to eight characters + memset(dest,0,8); + length = 0; + + while ((*src) && (*src != '.') && (++length<9)) + { + *dest++ = toupper(*src); + *src++; + } + /* cph - length check removed, just truncate at 8 chars. + * If there are 8 or more chars, we'll copy 8, and no zero termination + */ +} + +// +// 1/18/98 killough: adds a default extension to a path +// Note: Backslashes are treated specially, for MS-DOS. +// + +char *AddDefaultExtension(char *path, const char *ext) +{ + char *p = path; + while (*p++); + while (p-->path && *p!='/' && *p!='\\') + if (*p=='.') + return path; + if (*ext!='.') + strcat(path,"."); + return strcat(path,ext); +} + +// +// LUMP BASED ROUTINES. +// + +// +// W_AddFile +// All files are optional, but at least one file must be +// found (PWAD, if all required lumps are present). +// Files with a .wad extension are wadlink files +// with multiple lumps. +// Other files are single lumps with the base filename +// for the lump name. +// +// Reload hack removed by Lee Killough +// CPhipps - source is an enum +// +// proff - changed using pointer to wadfile_info_t +static void W_AddFile(wadfile_info_t *wadfile) +// killough 1/31/98: static, const +{ + wadinfo_t header; + lumpinfo_t* lump_p; + unsigned i; + int length; + int startlump; + filelump_t *fileinfo, *fileinfo2free=NULL; //killough + filelump_t singleinfo; + int flags = 0; + + if (wadfile->src == source_skip) + { + return; + } + + // open the file and add to directory + + wadfile->handle = M_open(wadfile->name,O_RDONLY | O_BINARY); + +#ifdef HAVE_NET + if (wadfile->handle == -1 && D_NetGetWad(wadfile->name)) // CPhipps + wadfile->handle = M_open(wadfile->name,O_RDONLY | O_BINARY); +#endif + + if (wadfile->handle == -1 && + strlen(wadfile->name) > 4 && + wadfile->src == source_pwad && + !strcasecmp(wadfile->name + strlen(wadfile->name) - 4 , ".wad") && + D_TryGetWad(wadfile->name)) + { + wadfile->handle = M_open(wadfile->name, O_RDONLY | O_BINARY); + } + + if (wadfile->handle == -1) + { + if ( strlen(wadfile->name)<=4 || // add error check -- killough + (strcasecmp(wadfile->name+strlen(wadfile->name)-4 , ".lmp" ) && + strcasecmp(wadfile->name+strlen(wadfile->name)-4 , ".gwa" ) ) + ) + I_Error("W_AddFile: couldn't open %s",wadfile->name); + return; + } + + //jff 8/3/98 use logical output routine + lprintf (LO_INFO," adding %s\n",wadfile->name); + startlump = numlumps; + + // mark lumps from internal resource + if (wadfile->src == source_auto_load) + { + int len = strlen(PACKAGE_TARNAME ".wad"); + int len_file = strlen(wadfile->name); + if (len_file >= len) + { + if (!strcasecmp(wadfile->name + len_file - len, PACKAGE_TARNAME ".wad")) + { + flags = LUMP_PRBOOM; + } + } + } + + if ( strlen(wadfile->name)<=4 || + ( + strcasecmp(wadfile->name+strlen(wadfile->name)-4,".wad") && + strcasecmp(wadfile->name+strlen(wadfile->name)-4,".gwa") + ) + ) + { + // single lump file + fileinfo = &singleinfo; + singleinfo.filepos = 0; + singleinfo.size = LittleLong(I_Filelength(wadfile->handle)); + ExtractFileBase(wadfile->name, singleinfo.name); + numlumps++; + } + else + { + // WAD file + I_Read(wadfile->handle, &header, sizeof(header)); + if (strncmp(header.identification,"IWAD",4) && + strncmp(header.identification,"PWAD",4)) + I_Error("W_AddFile: Wad file %s doesn't have IWAD or PWAD id", wadfile->name); + header.numlumps = LittleLong(header.numlumps); + header.infotableofs = LittleLong(header.infotableofs); + length = header.numlumps*sizeof(filelump_t); + fileinfo2free = fileinfo = malloc(length); // killough + lseek(wadfile->handle, header.infotableofs, SEEK_SET), + I_Read(wadfile->handle, fileinfo, length); + numlumps += header.numlumps; + } + + // Fill in lumpinfo + lumpinfo = realloc(lumpinfo, numlumps*sizeof(lumpinfo_t)); + + lump_p = &lumpinfo[startlump]; + + for (i=startlump ; (int)iflags = flags; + lump_p->wadfile = wadfile; // killough 4/25/98 + lump_p->position = LittleLong(fileinfo->filepos); + lump_p->size = LittleLong(fileinfo->size); + if (wadfile->src == source_lmp) + { + // Modifications to place command-line-added demo lumps + // into a separate "ns_demos" namespace so that they cannot + // conflict with other lump names + lump_p->li_namespace = ns_demos; + } + else + { + lump_p->li_namespace = ns_global; // killough 4/17/98 + } + strncpy (lump_p->name, fileinfo->name, 8); + lump_p->source = wadfile->src; // Ty 08/29/98 + } + + free(fileinfo2free); // killough +} + +// jff 1/23/98 Create routines to reorder the master directory +// putting all flats into one marked block, and all sprites into another. +// This will allow loading of sprites and flats from a PWAD with no +// other changes to code, particularly fast hashes of the lumps. +// +// killough 1/24/98 modified routines to be a little faster and smaller + +static int IsMarker(const char *marker, const char *name) +{ + return !strncasecmp(name, marker, 8) || + // doubled first character test for single-character prefixes only + // FF_* is valid alias for F_*, but HI_* should not allow HHI_* + (marker[1] == '_' && *name == *marker && !strncasecmp(name+1, marker, 7)); +} + +// killough 4/17/98: add namespace tags + +static int W_CoalesceMarkedResource(const char *start_marker, + const char *end_marker, li_namespace_e li_namespace) +{ + int result = 0; + lumpinfo_t *marked = malloc(sizeof(*marked) * numlumps); + size_t i, num_marked = 0, num_unmarked = 0; + int is_marked = 0, mark_end = 0; + lumpinfo_t *lump = lumpinfo; + + for (i=numlumps; i--; lump++) + if (IsMarker(start_marker, lump->name)) // start marker found + { // If this is the first start marker, add start marker to marked lumps + if (!num_marked) + { + strncpy(marked->name, start_marker, 8); + marked->size = 0; // killough 3/20/98: force size to be 0 + marked->li_namespace = ns_global; // killough 4/17/98 + marked->wadfile = NULL; + num_marked = 1; + } + is_marked = 1; // start marking lumps + } + else + if (IsMarker(end_marker, lump->name)) // end marker found + { + mark_end = 1; // add end marker below + is_marked = 0; // stop marking lumps + } + else + if (is_marked || lump->li_namespace == li_namespace) + { + // if we are marking lumps, + // move lump to marked list + // sf: check for namespace already set + + // sf 26/10/99: + // ignore sprite lumps smaller than 8 bytes (the smallest possible) + // in size -- this was used by some dmadds wads + // as an 'empty' graphics resource + if(li_namespace != ns_sprites || lump->size > 8) + { + marked[num_marked] = *lump; + marked[num_marked++].li_namespace = li_namespace; // killough 4/17/98 + result++; + } + } + else + lumpinfo[num_unmarked++] = *lump; // else move down THIS list + + // Append marked list to end of unmarked list + memcpy(lumpinfo + num_unmarked, marked, num_marked * sizeof(*marked)); + + free(marked); // free marked list + + numlumps = num_unmarked + num_marked; // new total number of lumps + + if (mark_end) // add end marker + { + lumpinfo[numlumps].size = 0; // killough 3/20/98: force size to be 0 + lumpinfo[numlumps].wadfile = NULL; + lumpinfo[numlumps].li_namespace = ns_global; // killough 4/17/98 + strncpy(lumpinfo[numlumps++].name, end_marker, 8); + } + + return result; +} + +// Hash function used for lump names. +// Must be mod'ed with table size. +// Can be used for any 8-character names. +// by Lee Killough + +unsigned W_LumpNameHash(const char *s) +{ + unsigned hash; + (void) ((hash = toupper(s[0]), s[1]) && + (hash = hash*3+toupper(s[1]), s[2]) && + (hash = hash*2+toupper(s[2]), s[3]) && + (hash = hash*2+toupper(s[3]), s[4]) && + (hash = hash*2+toupper(s[4]), s[5]) && + (hash = hash*2+toupper(s[5]), s[6]) && + (hash = hash*2+toupper(s[6]), + hash = hash*2+toupper(s[7])) + ); + return hash; +} + +// +// W_CheckNumForName +// Returns -1 if name not found. +// +// Rewritten by Lee Killough to use hash table for performance. Significantly +// cuts down on time -- increases Doom performance over 300%. This is the +// single most important optimization of the original Doom sources, because +// lump name lookup is used so often, and the original Doom used a sequential +// search. For large wads with > 1000 lumps this meant an average of over +// 500 were probed during every search. Now the average is under 2 probes per +// search. There is no significant benefit to packing the names into longwords +// with this new hashing algorithm, because the work to do the packing is +// just as much work as simply doing the string comparisons with the new +// algorithm, which minimizes the expected number of comparisons to under 2. +// +// killough 4/17/98: add namespace parameter to prevent collisions +// between different resources such as flats, sprites, colormaps +// + +// W_FindNumFromName, an iterative version of W_CheckNumForName +// returns list of lump numbers for a given name (latest first) +// +int (W_FindNumFromName)(const char *name, int li_namespace, int i) +{ + // Hash function maps the name to one of possibly numlump chains. + // It has been tuned so that the average chain length never exceeds 2. + + // proff 2001/09/07 - check numlumps==0, this happens when called before WAD loaded + if (numlumps == 0) + i = -1; + else + { + if (i < 0) + i = lumpinfo[W_LumpNameHash(name) % (unsigned) numlumps].index; + else + i = lumpinfo[i].next; + + // We search along the chain until end, looking for case-insensitive + // matches which also match a namespace tag. Separate hash tables are + // not used for each namespace, because the performance benefit is not + // worth the overhead, considering namespace collisions are rare in + // Doom wads. + + while (i >= 0 && (strncasecmp(lumpinfo[i].name, name, 8) || + lumpinfo[i].li_namespace != li_namespace)) + i = lumpinfo[i].next; + } + + // Return the matching lump, or -1 if none found. + + return i; +} + +// +// killough 1/31/98: Initialize lump hash table +// + +void W_HashLumps(void) +{ + int i; + + for (i=0; i= numlumps) + I_Error("W_GetLumpInfoByNum: lump num %d out of range", lump); + + return &lumpinfo[lump]; +} + +// W_CheckNumForNameInternal +// checks only internal resource +// +int W_CheckNumForNameInternal(const char *name) +{ + int p; + for (p = -1; (p = W_ListNumFromName(name, p)) >= 0; ) + { + if (lumpinfo[p].flags == LUMP_PRBOOM) + { + return p; + } + } + return -1; +} + +// W_ListNumFromName +// calls W_FindNumFromName and returns the lumps in ascending order +// +int W_ListNumFromName(const char *name, int lump) +{ + int i, next; + + for (i = -1; (next = W_FindNumFromName(name, i)) >= 0; i = next) + if (next == lump) + break; + + return i; +} + +// W_Init +// Loads each of the files in the wadfiles array. +// All files are optional, but at least one file +// must be found. +// Files with a .wad extension are idlink files +// with multiple lumps. +// Other files are single lumps with the base filename +// for the lump name. +// Lump names can appear multiple times. +// The name searcher looks backwards, so a later file +// does override all earlier ones. +// +// CPhipps - modified to use the new wadfiles array +// +wadfile_info_t *wadfiles=NULL; + +size_t numwadfiles = 0; // CPhipps - size of the wadfiles array (dynamic, no limit) + +void W_Init(void) +{ + // CPhipps - start with nothing + + numlumps = 0; lumpinfo = NULL; + + { // CPhipps - new wadfiles array used + // open all the files, load headers, and count lumps + int i; + for (i=0; (size_t)i 0) + { + close(wadfiles[i].handle); + wadfiles[i].handle = 0; + } + } + + numwadfiles = 0; + free(wadfiles); + wadfiles = NULL; + numlumps = 0; + free(lumpinfo); + lumpinfo = NULL; + + V_FreePlaypal(); +} + +// +// W_LumpLength +// Returns the buffer size needed to load the given lump. +// +int W_LumpLength (int lump) +{ + if (lump >= numlumps) + I_Error ("W_LumpLength: %i >= numlumps",lump); + return lumpinfo[lump].size; +} + +// +// W_ReadLump +// Loads the lump into the given buffer, +// which must be >= W_LumpLength(). +// + +void W_ReadLump(int lump, void *dest) +{ + lumpinfo_t *l = lumpinfo + lump; + +#ifdef RANGECHECK + if (lump >= numlumps) + I_Error ("W_ReadLump: %i >= numlumps",lump); +#endif + + { + if (l->wadfile) + { + lseek(l->wadfile->handle, l->position, SEEK_SET); + I_Read(l->wadfile->handle, dest, l->size); + } + } +} + diff --git a/src/w_wad.h b/src/w_wad.h new file mode 100644 index 0000000..e05efcd --- /dev/null +++ b/src/w_wad.h @@ -0,0 +1,171 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * WAD I/O functions. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __W_WAD__ +#define __W_WAD__ + +#ifdef __GNUG__ +#pragma interface +#endif + +// +// TYPES +// + +typedef struct +{ + char identification[4]; // Should be "IWAD" or "PWAD". + int numlumps; + int infotableofs; +} wadinfo_t; + +typedef struct +{ + int filepos; + int size; + char name[8]; +} filelump_t; + +// +// WADFILE I/O related stuff. +// + +// CPhipps - defined enum in wider scope +// Ty 08/29/98 - add source field to identify where this lump came from +typedef enum { + // CPhipps - define elements in order of 'how new/unusual' + source_skip = -1, + source_iwad=0, // iwad file load + source_pre, // predefined lump + source_auto_load, // lump auto-loaded by config file + source_pwad, // pwad file load + source_lmp, // lmp file load + source_net // CPhipps + + //e6y +// ,source_deh_auto_load + ,source_deh + ,source_err + +} wad_source_t; + +// CPhipps - changed wad init +// We _must_ have the wadfiles[] the same as those actually loaded, so there +// is no point having these separate entities. This belongs here. +typedef struct { + char* name; + wad_source_t src; + int handle; +} wadfile_info_t; + +extern wadfile_info_t *wadfiles; + +extern size_t numwadfiles; // CPhipps - size of the wadfiles array + +void W_Init(void); // CPhipps - uses the above array +void W_ReleaseAllWads(void); // Proff - Added for iwad switching +void W_InitCache(void); +void W_DoneCache(void); + +typedef enum +{ + ns_global=0, + ns_sprites, + ns_flats, + ns_colormaps, + ns_prboom, + ns_demos, + ns_hires //e6y +} li_namespace_e; // haleyjd 05/21/02: renamed from "namespace" + +typedef struct +{ + // WARNING: order of some fields important (see info.c). + + char name[9]; + int size; + + // killough 1/31/98: hash table fields, used for ultra-fast hash table lookup + int index, next; + + // killough 4/17/98: namespace tags, to prevent conflicts between resources + li_namespace_e li_namespace; // haleyjd 05/21/02: renamed from "namespace" + + wadfile_info_t *wadfile; + int position; + wad_source_t source; + int flags; //e6y +} lumpinfo_t; + +// e6y: lump flags +#define LUMP_STATIC 0x00000001 /* assigned gltexture should be static */ +#define LUMP_CM2RGB 0x00000002 /* for fake colormap for hires patches */ +#define LUMP_PRBOOM 0x00000004 /* from internal resource */ + +extern lumpinfo_t *lumpinfo; +extern int numlumps; + +// killough 4/17/98: if W_CheckNumForName() called with only +// one argument, pass ns_global as the default namespace + +#define W_FindNumFromName(name, lump) (W_FindNumFromName)(name, ns_global, lump) +int (W_FindNumFromName)(const char *name, int ns, int lump); +int W_CheckNumForNameInternal(const char *name); +int W_ListNumFromName(const char *name, int lump); +#define W_CheckNumForName(name) (W_CheckNumForName)(name, ns_global) +static inline +int (W_CheckNumForName)(const char *name, int ns) + { return (W_FindNumFromName)(name, ns, -1); } +int W_GetNumForName (const char* name); +const lumpinfo_t* W_GetLumpInfoByNum(int lump); +int W_LumpLength (int lump); +void W_ReadLump (int lump, void *dest); +// CPhipps - modified for 'new' lump locking +const void* W_CacheLumpNum (int lump); +const void* W_LockLumpNum(int lump); +void W_UnlockLumpNum(int lump); + +// CPhipps - convenience macros +//#define W_CacheLumpNum(num) (W_CacheLumpNum)((num),1) +#define W_CacheLumpName(name) W_CacheLumpNum (W_GetNumForName(name)) + +//#define W_UnlockLumpNum(num) (W_UnlockLumpNum)((num),1) +#define W_UnlockLumpName(name) W_UnlockLumpNum (W_GetNumForName(name)) + +char *AddDefaultExtension(char *, const char *); // killough 1/18/98 +void ExtractFileBase(const char *, char *); // killough +unsigned W_LumpNameHash(const char *s); // killough 1/31/98 +void W_HashLumps(void); // cph 2001/07/07 - made public + +#endif diff --git a/src/wi_stuff.c b/src/wi_stuff.c new file mode 100644 index 0000000..0dd96b9 --- /dev/null +++ b/src/wi_stuff.c @@ -0,0 +1,2159 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Intermission screens. + * + *----------------------------------------------------------------------------- + */ + +#include "doomstat.h" +#include "m_random.h" +#include "w_wad.h" +#include "g_game.h" +#include "r_main.h" +#include "v_video.h" +#include "wi_stuff.h" +#include "s_sound.h" +#include "sounds.h" +#include "lprintf.h" // jff 08/03/98 - declaration of lprintf +#include "r_draw.h" +#include "hu_stuff.h" + +// Ty 03/17/98: flag that new par times have been loaded in d_deh +extern dboolean deh_pars; +extern dboolean um_pars; + +// +// Data needed to add patches to full screen intermission pics. +// Patches are statistics messages, and animations. +// Loads of by-pixel layout and placement, offsets etc. +// + +// +// Different vetween registered DOOM (1994) and +// Ultimate DOOM - Final edition (retail, 1995?). +// This is supposedly ignored for commercial +// release (aka DOOM II), which had 34 maps +// in one episode. So there. +#define NUMEPISODES 4 +#define NUMMAPS 9 + + +// Not used +// 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 8 +// proff/nicolas 09/20/98 -- changed for hi-res +#define SP_TIMEY 160 +//#define SP_TIMEY (SCREENHEIGHT-32) + + +// NET GAME STUFF +#define NG_STATSY 50 +#define NG_STATSX (32 + V_NamePatchWidth(star)/2 + 32*!dofrags) + +#define NG_SPACINGX 64 + + +// Used to display the frags matrix at endgame +// 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 + + +// These animation variables, structures, etc. are used for the +// DOOM/Ultimate DOOM intermission screen animations. This is +// totally different from any sprite or texture/flat animations +typedef enum +{ + ANIM_ALWAYS, // determined by patch entry + ANIM_RANDOM, // occasional + ANIM_LEVEL // continuous +} animenum_t; + +typedef struct +{ + int x; // x/y coordinate pair structure + 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 + * cphipps - const + */ + patchnum_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; + + +static point_t lnodes[NUMEPISODES][NUMMAPS] = +{ + // Episode 0 World Map + { + { 185, 164 }, // location of level 0 (CJ) + { 148, 143 }, // location of level 1 (CJ) + { 69, 122 }, // location of level 2 (CJ) + { 209, 102 }, // location of level 3 (CJ) + { 116, 89 }, // location of level 4 (CJ) + { 166, 55 }, // location of level 5 (CJ) + { 71, 56 }, // location of level 6 (CJ) + { 135, 29 }, // location of level 7 (CJ) + { 71, 24 } // location of level 8 (CJ) + }, + + // Episode 1 World Map should go here + { + { 254, 25 }, // location of level 0 (CJ) + { 97, 50 }, // location of level 1 (CJ) + { 188, 64 }, // location of level 2 (CJ) + { 128, 78 }, // location of level 3 (CJ) + { 214, 92 }, // location of level 4 (CJ) + { 133, 130 }, // location of level 5 (CJ) + { 208, 136 }, // location of level 6 (CJ) + { 148, 140 }, // location of level 7 (CJ) + { 235, 158 } // location of level 8 (CJ) + }, + + // Episode 2 World Map should go here + { + { 156, 168 }, // location of level 0 (CJ) + { 48, 154 }, // location of level 1 (CJ) + { 174, 95 }, // location of level 2 (CJ) + { 265, 75 }, // location of level 3 (CJ) + { 130, 48 }, // location of level 4 (CJ) + { 279, 23 }, // location of level 5 (CJ) + { 198, 48 }, // location of level 6 (CJ) + { 140, 25 }, // location of level 7 (CJ) + { 281, 136 } // location of level 8 (CJ) + } +}; + + +// +// Animation locations for episode 0 (1). +// Using patches saves a lot of space, +// as they replace 320x200 full screen frames. +// +static anim_t epsd0animinfo[] = +{ + { ANIM_ALWAYS, TICRATE/3, 3, { 224, 104 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 184, 160 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 112, 136 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 72, 112 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 88, 96 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 64, 48 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 192, 40 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 136, 16 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 80, 16 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 64, 24 } } +}; + +static anim_t epsd1animinfo[] = +{ + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 1 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 2 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 3 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 4 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 5 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 6 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 7 }, + { ANIM_LEVEL, TICRATE/3, 3, { 192, 144 }, 8 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 8 } +}; + +static anim_t epsd2animinfo[] = +{ + { ANIM_ALWAYS, TICRATE/3, 3, { 104, 168 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 40, 136 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 160, 96 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 104, 80 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 120, 32 } }, + { ANIM_ALWAYS, TICRATE/4, 3, { 40, 0 } } +}; + +static int NUMANIMS[NUMEPISODES] = +{ + sizeof(epsd0animinfo)/sizeof(anim_t), + sizeof(epsd1animinfo)/sizeof(anim_t), + sizeof(epsd2animinfo)/sizeof(anim_t) +}; + +static anim_t *anims[NUMEPISODES] = +{ + epsd0animinfo, + epsd1animinfo, + epsd2animinfo +}; + + +// +// GENERAL DATA +// + +// +// Locally used stuff. +// +#define FB 0 + + +// States for single-player +#define SP_KILLS 0 +#define SP_ITEMS 2 +#define SP_SECRET 4 +#define SP_FRAGS 6 +#define SP_TIME 8 +#define SP_PAR ST_TIME + +#define SP_PAUSE 1 + +// in seconds +#define SHOWNEXTLOCDELAY 4 +//#define SHOWLASTLOCDELAY SHOWNEXTLOCDELAY + + +// used to accelerate or skip a stage +int acceleratestage; // killough 3/28/98: made global + +// 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_time; +static int cnt_total_time; +static int cnt_par; +static int cnt_pause; + +// +// GRAPHICS +// + +// You Are Here graphic +static const char* yah[3] = { "WIURH0", "WIURH1", 0 }; + +// splat +static const char* splat[2] = {"WISPLAT", 0}; + +// %, : graphics +static const char percent[] = {"WIPCNT"}; +static const char colon[] = {"WICOLON"}; + +// 0-9 graphic +static patchnum_t num[10]; + +// minus sign +static const char wiminus[] = {"WIMINUS"}; + +// "Finished!" graphics +static const char finished[] = {"WIF"}; + +// "Entering" graphic +static const char entering[] = {"WIENTER"}; + +// "secret" +static const char sp_secret[] = {"WISCRT2"}; + +// "Kills", "Scrt", "Items", "Frags" +static const char kills[] = {"WIOSTK"}; +static const char secret[] = {"WIOSTS"}; +static const char items[] = {"WIOSTI"}; +static const char frags[] = {"WIFRGS"}; + +// Time sucks. +static const char time1[] = {"WITIME"}; +static const char par[] = {"WIPAR"}; +static const char sucks[] = {"WISUCKS"}; + +// "killers", "victims" +static const char killers[] = {"WIKILRS"}; +static const char victims[] = {"WIVCTMS"}; + +// "Total", your face, your dead face +static const char total[] = {"WIMSTT"}; +static const char star[] = {"STFST01"}; +static const char bstar[] = {"STFDEAD0"}; + +// "red P[1..MAXPLAYERS]" +static const char facebackp[] = {"STPB0"}; + +static const char *exitpic, *enterpic; + +// +// CODE +// + +static void WI_endDeathmatchStats(void); +static void WI_endNetgameStats(void); +#define WI_endStats WI_endNetgameStats + +/* ==================================================================== + * WI_levelNameLump + * Purpore: Returns the name of the graphic lump containing the name of + * the given level. + * Args: Episode and level, and buffer (must by 9 chars) to write to + * Returns: void + */ +void WI_levelNameLump(int epis, int map, char* buf) +{ + if (gamemode == commercial) { + sprintf(buf, "CWILV%2.2d", map); + } else { + sprintf(buf, "WILV%d%d", epis, map); + } +} + +// ==================================================================== +// WI_slamBackground +// Purpose: Put the full-screen background up prior to patches +// Args: none +// Returns: void +// +static void WI_slamBackground(void) +{ + char name[9]; // limited to 8 characters + + if (state != StatCount && enterpic) strcpy(name, enterpic); + else if (exitpic) strcpy(name, exitpic); + else if (gamemode == commercial || wbs->epsd < 0 || (gamemode == retail && wbs->epsd >= 3)) + strcpy(name, "INTERPIC"); + else + sprintf(name, "WIMAP%d", wbs->epsd); + + // e6y: wide-res + V_FillBorder(-1, 0); + + // background + V_DrawNamePatch(0, 0, FB, name, CR_DEFAULT, VPT_STRETCH); +} + + +// ==================================================================== +// WI_Responder +// Purpose: Draw animations on intermission background screen +// Args: ev -- event pointer, not actually used here. +// Returns: False -- dummy routine +// +// The ticker is used to detect keys +// because of timing issues in netgames. +dboolean WI_Responder(event_t* ev) +{ + return false; +} + +#define SPACEWIDTH 4 +extern patchnum_t hu_font[HU_FONTSIZE]; + +static void WI_DrawString(int cx, int cy, const char* ch) +{ + int w; + int c; + const char *cc = ch; + int width = 0; + + // center the text. + while (*cc) { + c = *cc++; // get next char + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) + { + width += SPACEWIDTH; // space + continue; + } + width += hu_font[c].width; + } + cx -= width / 2; + if (cx < 0) cx = 0; + + + while (*ch) { + c = *ch++; // get next char + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) + { + cx += SPACEWIDTH; // space + continue; + } + w = hu_font[c].width; + if (cx + w > 320) + break; + + V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, CR_GRAY, VPT_STRETCH | VPT_TRANS); + cx += w; + } +} + +// ==================================================================== +// WI_drawLF +// Purpose: Draw the "Finished" level name before showing stats +// Args: none +// Returns: void +// +void WI_drawLF(void) +{ + int y = WI_TITLEY; + char lname[9]; + + if (wbs->lastmapinfo != NULL && wbs->lastmapinfo->levelname != NULL && wbs->lastmapinfo->levelpic[0] == 0) + { + // The level defines a new name but no texture for the name. + WI_DrawString(160, y, wbs->lastmapinfo->levelname); + y += (5 * hu_font['A' - HU_FONTSTART].height / 4); + + if (wbs->lastmapinfo->author) + { + WI_DrawString(160, y, wbs->lastmapinfo->author); + y += (5 * hu_font['A' - HU_FONTSTART].height / 4); + } + } + else + { + // draw + /* cph - get the graphic lump name and use it */ + if (wbs->lastmapinfo != NULL && wbs->lastmapinfo->levelpic[0]) strcpy(lname, wbs->lastmapinfo->levelpic); + else WI_levelNameLump(wbs->epsd, wbs->last, lname); + + if (W_CheckNumForName(lname) == -1) + return; + + // CPhipps - patch drawing updated + V_DrawNamePatch((320 - V_NamePatchWidth(lname)) / 2, y, + FB, lname, CR_DEFAULT, VPT_STRETCH); + + // draw "Finished!" + y += (5 * V_NamePatchHeight(lname)) / 4; + } + + + // CPhipps - patch drawing updated + V_DrawNamePatch((320 - V_NamePatchWidth(finished))/2, y, + FB, finished, CR_DEFAULT, VPT_STRETCH); +} + + +// ==================================================================== +// WI_drawEL +// Purpose: Draw introductory "Entering" and level name +// Args: none +// Returns: void +// +void WI_drawEL(void) +{ + int y = WI_TITLEY; + char lname[9]; + + // draw "Entering" + // CPhipps - patch drawing updated + V_DrawNamePatch((320 - V_NamePatchWidth(entering)) / 2, + y, FB, entering, CR_DEFAULT, VPT_STRETCH); + + + if (wbs->nextmapinfo != NULL && wbs->nextmapinfo->levelname != NULL && wbs->nextmapinfo->levelpic[0] == 0) + { + y += (5 * V_NamePatchHeight(entering)) / 4; + + // The level defines a new name but no texture for the name. + WI_DrawString(160, y, wbs->nextmapinfo->levelname); + + if (wbs->nextmapinfo->author) + { + y += (5 * hu_font['A' - HU_FONTSTART].height / 4); + + WI_DrawString(160, y, wbs->nextmapinfo->author); + } + + y += (5 * hu_font['A' - HU_FONTSTART].height / 4); + } + else + { + /* cph - get the graphic lump name */ + if (wbs->nextmapinfo != NULL && wbs->nextmapinfo->levelpic[0]) strcpy(lname, wbs->nextmapinfo->levelpic); + else WI_levelNameLump(wbs->nextep, wbs->next, lname); + + if (W_CheckNumForName(lname) == -1) + return; + + // draw level + y += (5 * V_NamePatchHeight(lname)) / 4; + + // CPhipps - patch drawing updated + V_DrawNamePatch((320 - V_NamePatchWidth(lname)) / 2, y, FB, + lname, CR_DEFAULT, VPT_STRETCH); + + y += (5 * V_NamePatchHeight(lname)) / 4; + } +} + +/* ==================================================================== + * WI_drawOnLnode + * Purpose: Draw patches at a location based on episode/map + * Args: n -- index to map# within episode + * c[] -- array of names of patches to be drawn + * Returns: void + */ +void +WI_drawOnLnode // draw stuff at a location by episode/map# +( int n, + const char* const c[] ) +{ + int i; + dboolean fits = false; + + i = 0; + do + { + int left; + int top; + int right; + int bottom; + const rpatch_t* patch = R_CachePatchName(c[i]); + + left = lnodes[wbs->epsd][n].x - patch->leftoffset; + top = lnodes[wbs->epsd][n].y - patch->topoffset; + right = left + patch->width; + bottom = top + patch->height; + R_UnlockPatchName(c[i]); + + if (left >= 0 + && right < 320 + && top >= 0 + && bottom < 200) + { + fits = true; + } + else + { + i++; + } + } while (!fits && i!=2 && c[i]); + + if (fits && i<2) + { + // CPhipps - patch drawing updated + V_DrawNamePatch(lnodes[wbs->epsd][n].x, lnodes[wbs->epsd][n].y, + FB, c[i], CR_DEFAULT, VPT_STRETCH); + } + else + { + // DEBUG + //jff 8/3/98 use logical output routine + lprintf(LO_DEBUG,"Could not place patch on level %d\n", n+1); + } +} + + +// ==================================================================== +// WI_initAnimatedBack +// Purpose: Initialize pointers and styles for background animation +// Args: none +// Returns: void +// +void WI_initAnimatedBack(int entering) +{ + int i; + anim_t* a; + + if (exitpic) return; + if (enterpic && entering) return; + + if (gamemode == commercial) // no animation for DOOM2 + return; + + if (wbs->epsd < 0 || wbs->epsd > 2) + return; + + + for (i=0;iepsd];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; + } +} + + +// ==================================================================== +// WI_updateAnimatedBack +// Purpose: Figure out what animation we do on this iteration +// Args: none +// Returns: void +// +void WI_updateAnimatedBack(void) +{ + int i; + anim_t* a; + + if (exitpic) return; + if (enterpic && state != StatCount) return; + + if (gamemode == commercial) + return; + + if (wbs->epsd < 0 || wbs->epsd > 2) + return; + + for (i=0;iepsd];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 == StatCount && i == 7) + && wbs->next == a->data1) + { + a->ctr++; + if (a->ctr == a->nanims) a->ctr--; + a->nexttic = bcnt + a->period; + } + break; + } + } + } +} + + +// ==================================================================== +// WI_drawAnimatedBack +// Purpose: Actually do the animation (whew!) +// Args: none +// Returns: void +// +void WI_drawAnimatedBack(void) +{ + int i; + anim_t* a; + + if (exitpic) return; + if (enterpic && state != StatCount) return; + + if (gamemode==commercial) //jff 4/25/98 Someone forgot commercial an enum + return; + + if (wbs->epsd < 0 || wbs->epsd > 2) + return; + + for (i=0 ; iepsd] ; i++) + { + a = &anims[wbs->epsd][i]; + + if (a->ctr >= 0) + // CPhipps - patch drawing updated + V_DrawNumPatch(a->loc.x, a->loc.y, FB, a->p[a->ctr].lumpnum, CR_DEFAULT, VPT_STRETCH); + } +} + + +// ==================================================================== +// WI_drawNum +// Purpose: Draws a number. If digits > 0, then use that many digits +// minimum, otherwise only use as many as necessary +// Args: x, y -- location +// n -- the number to be drawn +// digits -- number of digits minimum or zero +// Returns: new x position after drawing (note we are going to the left) +// CPhipps - static +static int WI_drawNum (int x, int y, int n, int digits) +{ + int fontwidth = 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; + // CPhipps - patch drawing updated + V_DrawNumPatch(x, y, FB, num[ n % 10 ].lumpnum, CR_DEFAULT, VPT_STRETCH); + n /= 10; + } + + // draw a minus sign if necessary + if (neg) + // CPhipps - patch drawing updated + V_DrawNamePatch(x-=8, y, FB, wiminus, CR_DEFAULT, VPT_STRETCH); + + return x; +} + + +// ==================================================================== +// WI_drawPercent +// Purpose: Draws a percentage, really just a call to WI_drawNum +// after putting a percent sign out there +// Args: x, y -- location +// p -- the percentage value to be drawn, no negatives +// Returns: void +// CPhipps - static +static void WI_drawPercent(int x, int y, int p) +{ + if (p < 0) + return; + + // CPhipps - patch drawing updated + V_DrawNamePatch(x, y, FB, percent, CR_DEFAULT, VPT_STRETCH); + WI_drawNum(x, y, p, -1); +} + + +// ==================================================================== +// WI_drawTime +// Purpose: Draws the level completion time or par time, or "Sucks" +// if 1 hour or more +// Args: x, y -- location +// t -- the time value to be drawn +// Returns: void +// +// CPhipps - static +// - largely rewritten to display hours and use slightly better algorithm + +static void WI_drawTime(int x, int y, int t) +{ + int n; + + if (t<0) + return; + + if (t < 100*60*60) + for(;;) { + n = t % 60; + t /= 60; + x = WI_drawNum(x, y, n, (t || n>9) ? 2 : 1) - V_NamePatchWidth(colon); + + // draw + if (t) + // CPhipps - patch drawing updated + V_DrawNamePatch(x, y, FB, colon, CR_DEFAULT, VPT_STRETCH); + else break; + } + else // "sucks" (maybe should be "addicted", even I've never had a 100 hour game ;) + V_DrawNamePatch(x - V_NamePatchWidth(sucks), + y, FB, sucks, CR_DEFAULT, VPT_STRETCH); +} + + +// ==================================================================== +// WI_End +// Purpose: Unloads data structures (inverse of WI_Start) +// Args: none +// Returns: void +// +void WI_End(void) +{ + if (deathmatch) + WI_endDeathmatchStats(); + else if (netgame) + WI_endNetgameStats(); + else + WI_endStats(); +} + + +// ==================================================================== +// WI_initNoState +// Purpose: Clear state, ready for end of level activity +// Args: none +// Returns: void +// +void WI_initNoState(void) +{ + state = NoState; + acceleratestage = 0; + cnt = 10; +} + + +// ==================================================================== +// WI_drawTimeStats +// Purpose: Put the times on the screen +// Args: time, total time, par time, in seconds +// Returns: void +// +// cph - pulled from WI_drawStats below + +static void WI_drawTimeStats(int cnt_time, int cnt_total_time, int cnt_par) +{ + V_DrawNamePatch(SP_TIMEX, SP_TIMEY, FB, time1, CR_DEFAULT, VPT_STRETCH); + WI_drawTime(320/2 - SP_TIMEX, SP_TIMEY, cnt_time); + + V_DrawNamePatch(SP_TIMEX, (SP_TIMEY+200)/2, FB, total, CR_DEFAULT, VPT_STRETCH); + WI_drawTime(320/2 - SP_TIMEX, (SP_TIMEY+200)/2, cnt_total_time); + + // Ty 04/11/98: redid logic: should skip only if with pwad but + // without deh patch + // killough 2/22/98: skip drawing par times on pwads + // Ty 03/17/98: unless pars changed with deh patch + + if (!(modifiedgame && !deh_pars && !um_pars) + || (gamemission == pack_nerve && singleplayer)) + { + if (wbs->epsd < 4 || um_pars) + { + V_DrawNamePatch(320/2 + SP_TIMEX, SP_TIMEY, FB, par, CR_DEFAULT, VPT_STRETCH); + WI_drawTime(320 - SP_TIMEX, SP_TIMEY, cnt_par); + } + } +} + +// ==================================================================== +// WI_updateNoState +// Purpose: Cycle until end of level activity is done +// Args: none +// Returns: void +// +void WI_updateNoState(void) +{ + + WI_updateAnimatedBack(); + + if (!--cnt) + G_WorldDone(); +} + +static dboolean snl_pointeron = false; + + +// ==================================================================== +// WI_initShowNextLoc +// Purpose: Prepare to show the next level's location +// Args: none +// Returns: void +// +void WI_initShowNextLoc(void) +{ + if (gamemapinfo != NULL) + { + if (gamemapinfo->endpic[0]) + { + G_WorldDone(); + return; + } + state = ShowNextLoc; + + // episode change + if (wbs->epsd != wbs->nextep) + { + void WI_loadData(void); + + wbs->epsd = wbs->nextep; + wbs->last = wbs->next - 1; + WI_loadData(); + } + } + else if ((gamemode != commercial) && (gamemap == 8)) { + G_WorldDone(); + return; + } + else + state = ShowNextLoc; + + acceleratestage = 0; + + // e6y: That was pretty easy - only a HEX editor and luck + // There is no more desync on ddt-tas.zip\e4tux231.lmp + // --------- tasdoom.idb --------- + // .text:00031194 loc_31194: ; CODE XREF: WI_updateStats+3A9j + // .text:00031194 mov ds:state, 1 + // .text:0003119E mov ds:acceleratestage, 0 + // .text:000311A8 mov ds:cnt, 3Ch + // nowhere no hide + if (compatibility_level == tasdoom_compatibility) + cnt = 60; + else + cnt = SHOWNEXTLOCDELAY * TICRATE; + + WI_initAnimatedBack(true); +} + + +// ==================================================================== +// WI_updateShowNextLoc +// Purpose: Prepare to show the next level's location +// Args: none +// Returns: void +// +void WI_updateShowNextLoc(void) +{ + WI_updateAnimatedBack(); + + if (!--cnt || acceleratestage) + WI_initNoState(); + else + snl_pointeron = (cnt & 31) < 20; +} + + +// ==================================================================== +// WI_drawShowNextLoc +// Purpose: Show the next level's location on animated backgrounds +// Args: none +// Returns: void +// +void WI_drawShowNextLoc(void) +{ + int i; + int last; + + if (gamemapinfo != NULL && + gamemapinfo->endpic[0] && + strcmp(gamemapinfo->endpic, "-") != 0) + { + return; + } + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + + // custom interpic. + if (exitpic || (enterpic && state != StatCount)) + { + WI_drawEL(); + return; + } + + if ( gamemode != commercial) + { + if (wbs->epsd < 0 || wbs->epsd > 2) + { + WI_drawEL(); // "Entering..." if not E1 or E2 + return; + } + + last = (wbs->last == 8) ? wbs->next - 1 : wbs->last; + + // draw a splat on taken cities. + for (i=0 ; i<=last ; i++) + WI_drawOnLnode(i, splat); + + // splat the secret level? + if (wbs->didsecret) + WI_drawOnLnode(8, splat); + + // draw flashing ptr + if (snl_pointeron) + WI_drawOnLnode(wbs->next, yah); + } + + if (gamemission == pack_nerve && singleplayer && wbs->last == 7) + return; // MAP08 end game + + // draws which level you are entering.. + if ( (gamemode != commercial) + || wbs->next != 30) // check for MAP30 end game + WI_drawEL(); +} + +// ==================================================================== +// WI_drawNoState +// Purpose: Draw the pointer and next location +// Args: none +// Returns: void +// +void WI_drawNoState(void) +{ + snl_pointeron = true; + WI_drawShowNextLoc(); +} + +// ==================================================================== +// WI_fragSum +// Purpose: Calculate frags for this player based on the current totals +// of all the other players. Subtract self-frags. +// Args: playernum -- the player to be calculated +// Returns: the total frags for this player +// +int WI_fragSum(int playernum) +{ + int i; + int frags = 0; + + for (i=0 ; i 999) // Ty 03/17/98 3-digit frag count + dm_frags[i][j] = 999; + + if (dm_frags[i][j] < -999) + dm_frags[i][j] = -999; + + stillticking = true; + } + } + dm_totals[i] = WI_fragSum(i); + + if (dm_totals[i] > 999) + dm_totals[i] = 999; + + if (dm_totals[i] < -999) + dm_totals[i] = -999; // Ty 03/17/98 end 3-digit frag count + } + } + + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + dm_state++; + } + } + else if (dm_state == 4) + { + if (acceleratestage) + { + S_StartSound(0, sfx_slop); + + if ( gamemode == commercial) + WI_initNoState(); + else + WI_initShowNextLoc(); + } + } + else if (dm_state & 1) + { + if (!--cnt_pause) + { + dm_state++; + cnt_pause = TICRATE; + } + } +} + + +// ==================================================================== +// WI_drawDeathmatchStats +// Purpose: Draw the stats on the screen in a matrix +// Args: none +// Returns: void +// +// proff/nicolas 09/20/98 -- changed for hi-res +// CPhipps - patch drawing updated +void WI_drawDeathmatchStats(void) +{ + int i; + int j; + int x; + int y; + int w; + + int lh; // line height + int halfface = V_NamePatchWidth(facebackp)/2; + + lh = WI_SPACINGY; + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + WI_drawLF(); + + // draw stat titles (top line) + V_DrawNamePatch(DM_TOTALSX-V_NamePatchWidth(total)/2, + DM_MATRIXY-WI_SPACINGY+10, FB, total, CR_DEFAULT, VPT_STRETCH); + + V_DrawNamePatch(DM_KILLERSX, DM_KILLERSY, FB, killers, CR_DEFAULT, VPT_STRETCH); + V_DrawNamePatch(DM_VICTIMSX, DM_VICTIMSY, FB, victims, CR_DEFAULT, VPT_STRETCH); + + // draw P? + x = DM_MATRIXX + DM_SPACINGX; + y = DM_MATRIXY; + + for (i=0 ; i 'int' for cnt_kills, cnt_items and cnt_secret +// +// Original sources use 'int' type for cnt_kills instead of 'short' +// I don't know who have made change of type, but this change +// leads to desynch if 'kills' percentage is more than 32767. +// Actually PrBoom will be in an infinite cycle at calculation of +// percentage if the player will not press for acceleration, because +// the condition (cnt_kills[0] >= (plrs[me].skills * 100) / wbs->maxkills) +// will be always false in this case. +// +// If you will kill 800 monsters on MAP30 on Ultra-Violence skill and +// will not press , vanilla will count up to 80000%, but PrBoom +// will be in infinite cycle of counting: +// (0, 1, 2, ..., 32766, 32767, -32768, -32767, ..., -1, 0, 1, ...) +// Negative numbers will not be displayed. + +static int *cnt_kills; +static int *cnt_items; +static int *cnt_secret; +static int *cnt_frags; +static int dofrags; +static int ng_state; + +// ==================================================================== +// CPhipps - WI_endNetgameStats +// Purpose: Clean up coop game stats +// Args: none +// Returns: void +// +static void WI_endNetgameStats(void) +{ + free(cnt_frags); cnt_frags = NULL; + free(cnt_secret); cnt_secret = NULL; + free(cnt_items); cnt_items = NULL; + free(cnt_kills); cnt_kills = NULL; +} + +// ==================================================================== +// WI_initNetgameStats +// Purpose: Prepare for coop game stats +// Args: none +// Returns: void +// +void WI_initNetgameStats(void) +{ + int i; + + state = StatCount; + acceleratestage = 0; + ng_state = 1; + + cnt_pause = TICRATE; + + // CPhipps - allocate these dynamically, blank with calloc + cnt_kills = calloc(MAXPLAYERS, sizeof(*cnt_kills)); + cnt_items = calloc(MAXPLAYERS, sizeof(*cnt_items)); + cnt_secret= calloc(MAXPLAYERS, sizeof(*cnt_secret)); + cnt_frags = calloc(MAXPLAYERS, sizeof(*cnt_frags)); + + for (i=0 ; imaxkills; + cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems; + + // killough 2/22/98: Make secrets = 100% if maxsecret = 0: + cnt_secret[i] = wbs->maxsecret ? + (plrs[i].ssecret * 100) / wbs->maxsecret : 100; + if (dofrags) + cnt_frags[i] = WI_fragSum(i); // we had frags + } + S_StartSound(0, sfx_barexp); // bang + ng_state = 10; + } + + if (ng_state == 2) + { + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); // pop + + stillticking = false; + + for (i=0 ; i= (plrs[i].skills * 100) / wbs->maxkills) + cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills; + else + stillticking = true; // still got stuff to tally + } + + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + ng_state++; + } + } + else if (ng_state == 4) + { + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i=0 ; i= (plrs[i].sitems * 100) / wbs->maxitems) + cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems; + else + stillticking = true; + } + + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + ng_state++; + } + } + else if (ng_state == 6) + { + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i=0 ; i= (wbs->maxsecret ? (plrs[i].ssecret * 100) / wbs->maxsecret : compatibility_level < lxdoom_1_compatibility ? 0 : 100)) + cnt_secret[i] = wbs->maxsecret ? (plrs[i].ssecret * 100) / wbs->maxsecret : 100; + else + stillticking = true; + } + + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + ng_state += 1 + 2*!dofrags; + } + } + else if (ng_state == 8) + { + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i=0 ; i= (fsum = WI_fragSum(i))) + cnt_frags[i] = fsum; + else + stillticking = true; + } + + if (!stillticking) + { + S_StartSound(0, sfx_pldeth); + ng_state++; + } + } + else if (ng_state == 10) + { + if (acceleratestage) + { + S_StartSound(0, sfx_sgcock); + if ( gamemode == commercial ) + WI_initNoState(); + else + WI_initShowNextLoc(); + } + } + else if (ng_state & 1) + { + if (!--cnt_pause) + { + ng_state++; + cnt_pause = TICRATE; + } + } +} + + +// ==================================================================== +// WI_drawNetgameStats +// Purpose: Put the coop stats on the screen +// Args: none +// Returns: void +// +// proff/nicolas 09/20/98 -- changed for hi-res +// CPhipps - patch drawing updated +void WI_drawNetgameStats(void) +{ + int i; + int x; + int y; + int pwidth = V_NamePatchWidth(percent); + int fwidth = V_NamePatchWidth(facebackp); + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + + WI_drawLF(); + + // draw stat titles (top line) + V_DrawNamePatch(NG_STATSX+NG_SPACINGX-V_NamePatchWidth(kills), + NG_STATSY, FB, kills, CR_DEFAULT, VPT_STRETCH); + + V_DrawNamePatch(NG_STATSX+2*NG_SPACINGX-V_NamePatchWidth(items), + NG_STATSY, FB, items, CR_DEFAULT, VPT_STRETCH); + + V_DrawNamePatch(NG_STATSX+3*NG_SPACINGX-V_NamePatchWidth(secret), + NG_STATSY, FB, secret, CR_DEFAULT, VPT_STRETCH); + + if (dofrags) + V_DrawNamePatch(NG_STATSX+4*NG_SPACINGX-V_NamePatchWidth(frags), + NG_STATSY, FB, frags, CR_DEFAULT, VPT_STRETCH); + + // draw stats + y = NG_STATSY + V_NamePatchHeight(kills); + + for (i=0 ; itotaltimes / TICRATE, wbs->partime / TICRATE); +} + +static int sp_state; + +// ==================================================================== +// WI_initStats +// Purpose: Get ready for single player stats +// Args: none +// Returns: void +// Comment: Seems like we could do all these stats in a more generic +// set of routines that weren't duplicated for dm, coop, sp +// +void WI_initStats(void) +{ + state = StatCount; + acceleratestage = 0; + sp_state = 1; + + // CPhipps - allocate (awful code, I know, but saves changing it all) and initialise + *(cnt_kills = malloc(sizeof(*cnt_kills))) = + *(cnt_items = malloc(sizeof(*cnt_items))) = + *(cnt_secret= malloc(sizeof(*cnt_secret))) = -1; + cnt_time = cnt_par = cnt_total_time = -1; + cnt_pause = TICRATE; + + WI_initAnimatedBack(false); +} + +// ==================================================================== +// WI_updateStats +// Purpose: Calculate solo stats +// Args: none +// Returns: void +// +void WI_updateStats(void) +{ + //e6y + static dboolean play_early_explosion = true; + + WI_updateAnimatedBack(); + + if (acceleratestage && sp_state != 10) + { + acceleratestage = 0; + cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; + cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; + + // killough 2/22/98: Make secrets = 100% if maxsecret = 0: + cnt_secret[0] = (wbs->maxsecret ? + (plrs[me].ssecret * 100) / wbs->maxsecret : 100); + + cnt_total_time = wbs->totaltimes / TICRATE; + cnt_time = plrs[me].stime / TICRATE; + cnt_par = wbs->partime / TICRATE; + S_StartSound(0, sfx_barexp); + sp_state = 10; + } + + if (sp_state == 2) + { + cnt_kills[0] += 2; + + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + if (cnt_kills[0] >= (plrs[me].skills * 100) / wbs->maxkills) + { + cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + else if (sp_state == 4) + { + cnt_items[0] += 2; + + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + if (cnt_items[0] >= (plrs[me].sitems * 100) / wbs->maxitems) + { + cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + else if (sp_state == 6) + { + cnt_secret[0] += 2; + + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + // killough 2/22/98: Make secrets = 100% if maxsecret = 0: + if ((!wbs->maxsecret && compatibility_level < lxdoom_1_compatibility) || + cnt_secret[0] >= (wbs->maxsecret ? + (plrs[me].ssecret * 100) / wbs->maxsecret : 100)) + { + cnt_secret[0] = (wbs->maxsecret ? + (plrs[me].ssecret * 100) / wbs->maxsecret : 100); + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + else if (sp_state == 8) + { + if (!(bcnt&3) && play_early_explosion) //e6y: do not play count sound after explosion sound + S_StartSound(0, sfx_pistol); + + cnt_time += 3; + + if (cnt_time >= plrs[me].stime / TICRATE) + cnt_time = plrs[me].stime / TICRATE; + + cnt_total_time += 3; + + if (cnt_total_time >= wbs->totaltimes / TICRATE) + cnt_total_time = wbs->totaltimes / TICRATE; + + cnt_par += 3; + + // e6y + // if par time is hidden (if modifiedgame is true) + // the game should play explosion sound immediately after + // the counter will reach level time instead of par time + if (modifiedgame && play_early_explosion) + { + if ((cnt_time >= plrs[me].stime / TICRATE) && (compatibility_level < lxdoom_1_compatibility || cnt_total_time >= wbs->totaltimes / TICRATE)) + { + // for ExM8 levels if the player won't have pressed + if (compatibility_level < lxdoom_1_compatibility) + cnt_total_time = wbs->totaltimes / TICRATE; + + S_StartSound(0, sfx_barexp); + play_early_explosion = false; // do not play it twice or more + } + } + + if (cnt_par >= wbs->partime / TICRATE) + { + cnt_par = wbs->partime / TICRATE; + + if ((cnt_time >= plrs[me].stime / TICRATE) && (compatibility_level < lxdoom_1_compatibility || cnt_total_time >= wbs->totaltimes / TICRATE)) + { + //e6y: for ExM8 levels + if (compatibility_level < lxdoom_1_compatibility) + cnt_total_time = wbs->totaltimes / TICRATE; + + if (!modifiedgame) //e6y: do not play explosion sound if it was already played + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + } + else if (sp_state == 10) + { + if (acceleratestage) + { + S_StartSound(0, sfx_sgcock); + + if (gamemode == commercial) + WI_initNoState(); + else + WI_initShowNextLoc(); + } + } + else if (sp_state & 1) + { + play_early_explosion = true; //e6y + if (!--cnt_pause) + { + sp_state++; + cnt_pause = TICRATE; + } + } +} + +// ==================================================================== +// WI_drawStats +// Purpose: Put the solo stats on the screen +// Args: none +// Returns: void +// +// proff/nicolas 09/20/98 -- changed for hi-res +// CPhipps - patch drawing updated +void WI_drawStats(void) +{ + // line height + int lh; + + lh = (3*num[0].height)/2; + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + + WI_drawLF(); + + V_DrawNamePatch(SP_STATSX, SP_STATSY, FB, kills, CR_DEFAULT, VPT_STRETCH); + if (cnt_kills) + WI_drawPercent(320 - SP_STATSX, SP_STATSY, cnt_kills[0]); + + V_DrawNamePatch(SP_STATSX, SP_STATSY+lh, FB, items, CR_DEFAULT, VPT_STRETCH); + if (cnt_items) + WI_drawPercent(320 - SP_STATSX, SP_STATSY+lh, cnt_items[0]); + + V_DrawNamePatch(SP_STATSX, SP_STATSY+2*lh, FB, sp_secret, CR_DEFAULT, VPT_STRETCH); + if (cnt_secret) + WI_drawPercent(320 - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0]); + + WI_drawTimeStats(cnt_time, cnt_total_time, cnt_par); +} + +// ==================================================================== +// WI_checkForAccelerate +// Purpose: See if the player has hit either the attack or use key +// or mouse button. If so we set acceleratestage to 1 and +// all those display routines above jump right to the end. +// Args: none +// Returns: void +// +void WI_checkForAccelerate(void) +{ + int i; + player_t *player; + + // check for button presses to skip delays + for (i=0, player = players ; icmd.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; + } + } +} + +// ==================================================================== +// WI_Ticker +// Purpose: Do various updates every gametic, for stats, animation, +// checking that intermission music is running, etc. +// Args: none +// Returns: void +// +void WI_Ticker(void) +{ + // counter for general background animation + bcnt++; + + if (bcnt == 1) + { + // intermission music + if ( gamemode == commercial ) + S_ChangeMusic(mus_dm2int, true); + else + S_ChangeMusic(mus_inter, true); + } + + WI_checkForAccelerate(); + + switch (state) + { + case StatCount: + if (deathmatch) WI_updateDeathmatchStats(); + else if (netgame) WI_updateNetgameStats(); + else WI_updateStats(); + break; + + case ShowNextLoc: + WI_updateShowNextLoc(); + break; + + case NoState: + WI_updateNoState(); + break; + } +} + +/* ==================================================================== + * WI_loadData + * Purpose: Initialize intermission data such as background graphics, + * patches, map names, etc. + * Args: none + * Returns: void + * + * CPhipps - modified for new wad lump handling. + * - no longer preload most graphics, other funcs can use + * them by name + */ + +void WI_loadData(void) +{ + int i; + int j; + char name[9]; // limited to 8 characters + anim_t* a; + + if (gamemode != commercial) + { + if (wbs->epsd < 3) + { + for (j=0;jepsd];j++) + { + a = &anims[wbs->epsd][j]; + for (i=0;inanims;i++) + { + // MONDO HACK! + if (wbs->epsd != 1 || j != 8) + { + // animations + snprintf(name, sizeof(name), "WIA%d%.2d%.2d", wbs->epsd, j, i); + R_SetPatchNum(&a->p[i], name); + } + else + { + // HACK ALERT! + a->p[i] = anims[1][4].p[i]; + } + } + } + } + } + + for (i=0;i<10;i++) + { + // numbers 0-9 + sprintf(name, "WINUM%d", i); + R_SetPatchNum(&num[i], name); + } +} + + +// ==================================================================== +// WI_Drawer +// Purpose: Call the appropriate stats drawing routine depending on +// what kind of game is being played (DM, coop, solo) +// Args: none +// Returns: void +// +void WI_Drawer (void) +{ + switch (state) + { + case StatCount: + if (deathmatch) + WI_drawDeathmatchStats(); + else if (netgame) + WI_drawNetgameStats(); + else + WI_drawStats(); + break; + + case ShowNextLoc: + WI_drawShowNextLoc(); + break; + + case NoState: + WI_drawNoState(); + break; + } +} + + +// ==================================================================== +// WI_initVariables +// Purpose: Initialize the intermission information structure +// Note: wbstartstruct_t is defined in d_player.h +// Args: wbstartstruct -- pointer to the structure with the data +// Returns: void +// +void WI_initVariables(wbstartstruct_t* wbstartstruct) +{ + + wbs = wbstartstruct; + +#ifdef RANGECHECKING + if (gamemode != commercial) + { + if ( gamemode == retail ) + RNGCHECK(wbs->epsd, 0, 3); + else + RNGCHECK(wbs->epsd, 0, 2); + } + else + { + RNGCHECK(wbs->last, 0, 8); + RNGCHECK(wbs->next, 0, 8); + } + RNGCHECK(wbs->pnum, 0, MAXPLAYERS); + RNGCHECK(wbs->pnum, 0, MAXPLAYERS); +#endif + + acceleratestage = 0; + cnt = bcnt = 0; + firstrefresh = 1; + me = wbs->pnum; + plrs = wbs->plyr; + + if (!wbs->maxkills) + wbs->maxkills = 1; // probably only useful in MAP30 + + if (!wbs->maxitems) + wbs->maxitems = 1; + + if ( gamemode != retail ) + if (wbs->epsd > 2) + wbs->epsd -= 3; +} + +// ==================================================================== +// WI_Start +// Purpose: Call the various init routines +// Note: wbstartstruct_t is defined in d_player.h +// Args: wbstartstruct -- pointer to the structure with the +// intermission data +// Returns: void +// +void WI_Start(wbstartstruct_t* wbstartstruct) +{ + WI_initVariables(wbstartstruct); + WI_loadData(); + + exitpic = (wbs->lastmapinfo && wbs->lastmapinfo->exitpic[0]) ? wbs->lastmapinfo->exitpic : NULL; + enterpic = (wbs->nextmapinfo && wbs->nextmapinfo->enterpic[0]) ? wbs->nextmapinfo->enterpic : NULL; + + if (deathmatch) + WI_initDeathmatchStats(); + else if (netgame) + WI_initNetgameStats(); + else + WI_initStats(); +} diff --git a/src/wi_stuff.h b/src/wi_stuff.h new file mode 100644 index 0000000..c3363c8 --- /dev/null +++ b/src/wi_stuff.h @@ -0,0 +1,64 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Intermission screens. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __WI_STUFF__ +#define __WI_STUFF__ + +//#include "v_video.h" + +#include "doomdef.h" + +// States for the intermission + +typedef enum +{ + NoState = -1, + StatCount, + ShowNextLoc + +} stateenum_t; + +// Called by main loop, animate the intermission. +void WI_Ticker (void); + +// Called by main loop, +// draws the intermission directly into the screen buffer. +void WI_Drawer (void); + +// Setup for an intermission screen. +void WI_Start(wbstartstruct_t* wbstartstruct); + +// Release intermission screen memory +void WI_End(void); + +#endif diff --git a/src/xs_Float.h b/src/xs_Float.h new file mode 100644 index 0000000..930d434 --- /dev/null +++ b/src/xs_Float.h @@ -0,0 +1,81 @@ +// ==================================================================================================================== +// ==================================================================================================================== +// xs_Float.h +// +// Source: "Know Your FPU: Fixing Floating Fast" +// http://www.stereopsis.com/sree/fpu2006.html +// +// xs_CRoundToInt: Round toward nearest, but ties round toward even (just like FISTP) +// xs_ToInt: Round toward zero, just like the C (int) cast +// xs_FloorToInt: Round down +// xs_CeilToInt: Round up +// xs_RoundToInt: Round toward nearest, but ties round up +// ==================================================================================================================== +// ==================================================================================================================== +#ifndef _xs_FLOAT_H_ +#define _xs_FLOAT_H_ + +#include "config.h" + +// ==================================================================================================================== +// Defines +// ==================================================================================================================== +#ifndef _xs_DEFAULT_CONVERSION +#define _xs_DEFAULT_CONVERSION 0 +#endif //_xs_DEFAULT_CONVERSION + + +#ifdef WORDS_BIGENDIAN + #define _xs_iexp_ 0 + #define _xs_iman_ 1 +#else + #define _xs_iexp_ 1 //intel is little endian + #define _xs_iman_ 0 +#endif //BigEndian_ + +#ifdef __GNUC__ +#define finline inline __attribute__ ((__always_inline__)) +#else +#define finline __forceinline +#endif + +typedef double real64; + + +typedef union _xs_doubleints +{ + real64 val; + unsigned ival[2]; +} _xs_doubleints; + + +// ==================================================================================================================== +// ==================================================================================================================== +// Inline implementation +// ==================================================================================================================== +// ==================================================================================================================== +finline int xs_CRoundToInt(real64 val) +{ +#if _xs_DEFAULT_CONVERSION==0 + _xs_doubleints uval; + uval.val = val + 6755399441055744.0; + return uval.ival[_xs_iman_]; +#else + return int(floor(val+.5)); +#endif +} + + +// ==================================================================================================================== +// ==================================================================================================================== +// Unsigned variants +// ==================================================================================================================== +// ==================================================================================================================== +finline unsigned xs_CRoundToUInt(real64 val) +{ + return (unsigned)xs_CRoundToInt(val); +} + +// ==================================================================================================================== +// ==================================================================================================================== +#endif // _xs_FLOAT_H_ diff --git a/src/z_bmalloc.c b/src/z_bmalloc.c new file mode 100644 index 0000000..b415381 --- /dev/null +++ b/src/z_bmalloc.c @@ -0,0 +1,123 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * This is designed to be a fast allocator for small, regularly used block sizes + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomtype.h" +#include "z_zone.h" +#include "z_bmalloc.h" +#include "lprintf.h" + +typedef struct bmalpool_s { + struct bmalpool_s *nextpool; + size_t blocks; + byte used[0]; +} bmalpool_t; + +inline static void* getelem(bmalpool_t *p, size_t size, size_t n) +{ + return (((byte*)p) + sizeof(bmalpool_t) + sizeof(byte)*(p->blocks) + size*n); +} + +inline static PUREFUNC int iselem(const bmalpool_t *pool, size_t size, const void* p) +{ + // CPhipps - need portable # of bytes between pointers + int dif = (const char*)p - (const char*)pool; + + dif -= sizeof(bmalpool_t); + dif -= pool->blocks; + if (dif<0) return -1; + dif /= size; + return (((size_t)dif >= pool->blocks) ? -1 : dif); +} + +enum { unused_block = 0, used_block = 1}; + +void* Z_BMalloc(struct block_memory_alloc_s *pzone) +{ + register bmalpool_t **pool = (bmalpool_t **)&(pzone->firstpool); + while (*pool != NULL) { + byte *p = memchr((*pool)->used, unused_block, (*pool)->blocks); // Scan for unused marker + if (p) { + int n = p - (*pool)->used; +#ifdef SIMPLECHECKS + if ((n<0) || ((size_t)n>=(*pool)->blocks)) + I_Error("Z_BMalloc: memchr returned pointer outside of array"); +#endif + (*pool)->used[n] = used_block; + return getelem(*pool, pzone->size, n); + } else + pool = &((*pool)->nextpool); + } + { + // Nothing available, must allocate a new pool + bmalpool_t *newpool; + + // CPhipps: Allocate new memory, initialised to 0 + + *pool = newpool = Z_Calloc(sizeof(*newpool) + (sizeof(byte) + pzone->size)*(pzone->perpool), + 1, pzone->tag, NULL); + newpool->nextpool = NULL; // NULL = (void*)0 so this is redundant + + // Return element 0 from this pool to satisfy the request + newpool->used[0] = used_block; + newpool->blocks = pzone->perpool; + return getelem(newpool, pzone->size, 0); + } +} + +void Z_BFree(struct block_memory_alloc_s *pzone, void* p) +{ + register bmalpool_t **pool = (bmalpool_t**)&(pzone->firstpool); + + while (*pool != NULL) { + int n = iselem(*pool, pzone->size, p); + if (n >= 0) { +#ifdef SIMPLECHECKS + if ((*pool)->used[n] == unused_block) + I_Error("Z_BFree: Refree in zone %s", pzone->desc); +#endif + (*pool)->used[n] = unused_block; + if (memchr(((*pool)->used), used_block, (*pool)->blocks) == NULL) { + // Block is all unused, can be freed + bmalpool_t *oldpool = *pool; + *pool = (*pool)->nextpool; + Z_Free(oldpool); + } + return; + } else pool = &((*pool)->nextpool); + } + I_Error("Z_BFree: Free not in zone %s", pzone->desc); +} diff --git a/src/z_bmalloc.h b/src/z_bmalloc.h new file mode 100644 index 0000000..fe0c949 --- /dev/null +++ b/src/z_bmalloc.h @@ -0,0 +1,57 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Block memory allocator + * This is designed to be a fast allocator for small, regularly used block sizes + *-----------------------------------------------------------------------------*/ + +#ifndef __Z_BMALLOC__ +#define __Z_BMALLOC__ + +struct block_memory_alloc_s { + void *firstpool; + size_t size; + size_t perpool; + int tag; + const char *desc; +}; + +#define DECLARE_BLOCK_MEMORY_ALLOC_ZONE(name) extern struct block_memory_alloc_s name +#define IMPLEMENT_BLOCK_MEMORY_ALLOC_ZONE(name, size, tag, num, desc) \ +struct block_memory_alloc_s name = { NULL, size, num, tag, desc} +#define NULL_BLOCK_MEMORY_ALLOC_ZONE(name) name.firstpool = NULL + +void* Z_BMalloc(struct block_memory_alloc_s *pzone); + +inline static void* Z_BCalloc(struct block_memory_alloc_s *pzone) +{ void *p = Z_BMalloc(pzone); memset(p,0,pzone->size); return p; } + +void Z_BFree(struct block_memory_alloc_s *pzone, void* p); + +#endif //__Z_BMALLOC__ diff --git a/src/z_zone.c b/src/z_zone.c new file mode 100644 index 0000000..b040ef3 --- /dev/null +++ b/src/z_zone.c @@ -0,0 +1,712 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Zone Memory Allocation. Neat. + * + * Neat enough to be rewritten by Lee Killough... + * + * Must not have been real neat :) + * + * Made faster and more general, and added wrappers for all of Doom's + * memory allocation functions, including malloc() and similar functions. + * Added line and file numbers, in case of error. Added performance + * statistics and tunables. + *----------------------------------------------------------------------------- + */ + + +// use config.h if autoconf made one -- josh +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "z_zone.h" +#include "doomstat.h" +#include "m_argv.h" +#include "v_video.h" +#include "g_game.h" +#include "lprintf.h" + +#ifdef DJGPP +#include +#endif + +#include "m_io.h" + +// Tunables + +// Alignment of zone memory (benefit may be negated by HEADER_SIZE, CHUNK_SIZE) +#define CACHE_ALIGN 32 + +// Minimum chunk size at which blocks are allocated +#define CHUNK_SIZE 32 + +// Minimum size a block must be to become part of a split +#define MIN_BLOCK_SPLIT (1024) + +// How much RAM to leave aside for other libraries +#define LEAVE_ASIDE (128*1024) + +// Amount to subtract when retrying failed attempts to allocate initial pool +#define RETRY_AMOUNT (256*1024) + +// signature for block header +#define ZONEID 0x931d4a11 + +// Number of mallocs & frees kept in history buffer (must be a power of 2) +#define ZONE_HISTORY 4 + +// End Tunables + +typedef struct memblock { + +#ifdef ZONEIDCHECK + unsigned id; +#endif + + struct memblock *next,*prev; + size_t size; + void **user; + unsigned char tag; + +#ifdef INSTRUMENTED + const char *file; + int line; +#endif + +} memblock_t; + +/* size of block header + * cph - base on sizeof(memblock_t), which can be larger than CHUNK_SIZE on + * 64bit architectures */ +static const size_t HEADER_SIZE = (sizeof(memblock_t)+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); + +static memblock_t *blockbytag[PU_MAX]; + +// 0 means unlimited, any other value is a hard limit +//static int memory_size = 8192*1024; +static int memory_size = 0; +static int free_memory = 0; + +#ifdef INSTRUMENTED + +// statistics for evaluating performance +static int active_memory = 0; +static int purgable_memory = 0; + +static void Z_DrawStats(void) // Print allocation statistics +{ + if (gamestate != GS_LEVEL) + return; + + if (memory_size > 0) { + unsigned long total_memory = free_memory + memory_size + active_memory + purgable_memory; + double s = 100.0 / total_memory; + + doom_printf("%-5i\t%6.01f%%\tstatic\n" + "%-5i\t%6.01f%%\tpurgable\n" + "%-5i\t%6.01f%%\tfree\n" + "%-5li\t\ttotal\n", + active_memory, + active_memory*s, + purgable_memory, + purgable_memory*s, + (free_memory + memory_size), + (free_memory + memory_size)*s, + total_memory + ); + } else { + unsigned long total_memory = active_memory + purgable_memory; + double s = 100.0 / total_memory; + + doom_printf("%-5i\t%6.01f%%\tstatic\n" + "%-5i\t%6.01f%%\tpurgable\n" + "%-5li\t\ttotal\n", + active_memory, + active_memory*s, + purgable_memory, + purgable_memory*s, + total_memory + ); + } +} + +#ifdef HEAPDUMP + +#ifndef HEAPDUMP_DIR +#define HEAPDUMP_DIR "." +#endif + +void W_PrintLump(FILE* fp, void* p); + +void Z_DumpMemory(void) +{ + static int dump; + char *buf; + int len; + FILE* fp; + size_t total_cache = 0, total_free = 0, total_malloc = 0; + int tag; + + len = doom_snprintf(NULL, 0, "%s/memdump.%d", HEAPDUMP_DIR, dump); + buf = malloc(len+1); + doom_snprintf(buf, len+1, "%s/memdump.%d", HEAPDUMP_DIR, dump); + fp = M_fopen(buf, "w"); + for (tag = PU_FREE; tag < PU_MAX; tag++) + { + memblock_t* end_block, *block; + block = blockbytag[tag]; + if (!block) + continue; + end_block = block->prev; + while (1) + { + switch (block->tag) { + case PU_FREE: + fprintf(fp, "free %d\n", block->size); + total_free += block->size; + break; + case PU_CACHE: + fprintf(fp, "cache %s:%d:%d\n", block->file, block->line, block->size); + total_cache += block->size; + break; + case PU_LEVEL: + fprintf(fp, "level %s:%d:%d\n", block->file, block->line, block->size); + total_malloc += block->size; + break; + default: + fprintf(fp, "malloc %s:%d:%d", block->file, block->line, block->size); + total_malloc += block->size; + if (block->file) + if (strstr(block->file,"w_memcache.c")) + W_PrintLump(fp, (char*)block + HEADER_SIZE); + fputc('\n', fp); + break; + } + if (block == end_block) + break; + block=block->next; + } + } + fprintf(fp, "malloc %d, cache %d, free %d, total %d\n", + total_malloc, total_cache, total_free, + total_malloc + total_cache + total_free); + fclose(fp); + free(buf); + dump++; +} +#endif +#endif + +#ifdef INSTRUMENTED + +// killough 4/26/98: Add history information + +enum {malloc_history, free_history, NUM_HISTORY_TYPES}; + +static const char *file_history[NUM_HISTORY_TYPES][ZONE_HISTORY]; +static int line_history[NUM_HISTORY_TYPES][ZONE_HISTORY]; +static int history_index[NUM_HISTORY_TYPES]; +static const char *const desc[NUM_HISTORY_TYPES] = {"malloc()'s", "free()'s"}; + +void Z_DumpHistory(char *buf) +{ + int i,j; + char s[1024]; + strcat(buf,"\n"); + for (i=0;i= sizeof(memblock_t) && size > HEADER_SIZE)) + I_Error("Z_Init: Sanity check failed"); +#endif + + size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size + size += HEADER_SIZE + CACHE_ALIGN; + + // Allocate the memory + + zonebase=(malloc)(size); + if (!zonebase) + I_Error("Z_Init: Failed on allocation of %lu bytes", (unsigned long)size); + + lprintf(LO_INFO,"Z_Init : Allocated %lukb zone memory\n", + (long unsigned)size / 1000); + + // Align on cache boundary + + zone = (memblock_t *) ((char *) zonebase + CACHE_ALIGN - + ((unsigned) zonebase & (CACHE_ALIGN-1))); + + rover = zone; // Rover points to base of zone mem + zone->next = zone->prev = zone; // Single node + zone->size = size; // All memory in one block + zone->tag = PU_FREE; // A free block + zone->vm = 0; + +#ifdef ZONEIDCHECK + zone->id = 0; +#endif + +#ifdef INSTRUMENTED + free_memory = size; + /* cph - remove unnecessary initialisations to 0 */ +#endif +#ifdef HEAPDUMP + I_AtExit(Z_DumpMemory, true); +#endif +#endif +} + +/* Z_Malloc + * You can pass a NULL user if the tag is < PU_PURGELEVEL. + * + * cph - the algorithm here was a very simple first-fit round-robin + * one - just keep looping around, freeing everything we can until + * we get a large enough space + * + * This has been changed now; we still do the round-robin first-fit, + * but we only free the blocks we actually end up using; we don't + * free all the stuff we just pass on the way. + */ + +void *(Z_Malloc)(size_t size, int tag, void **user +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + memblock_t *block = NULL; + +#ifdef INSTRUMENTED +#ifdef CHECKHEAP + Z_CheckHeap(); +#endif + + file_history[malloc_history][history_index[malloc_history]] = file; + line_history[malloc_history][history_index[malloc_history]++] = line; + history_index[malloc_history] &= ZONE_HISTORY-1; +#endif + +#ifdef ZONEIDCHECK + if (tag >= PU_PURGELEVEL && !user) + I_Error ("Z_Malloc: An owner is required for purgable blocks" +#ifdef INSTRUMENTED + "Source: %s:%d", file, line +#endif + ); +#endif + + if (!size) + return user ? *user = NULL : NULL; // malloc(0) returns NULL + + size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size + + if (memory_size > 0 && ((free_memory + memory_size) < (int)(size + HEADER_SIZE))) + { + memblock_t *end_block; + block = blockbytag[PU_CACHE]; + if (block) + { + end_block = block->prev; + while (1) + { + memblock_t *next = block->next; +#ifdef INSTRUMENTED + (Z_Free)((char *) block + HEADER_SIZE, file, line); +#else + (Z_Free)((char *) block + HEADER_SIZE); +#endif + if (((free_memory + memory_size) >= (int)(size + HEADER_SIZE)) || (block == end_block)) + break; + block = next; // Advance to next block + } + } + block = NULL; + } + +#ifdef HAVE_LIBDMALLOC + while (!(block = dmalloc_malloc(file,line,size + HEADER_SIZE,DMALLOC_FUNC_MALLOC,0,0))) { +#else + while (!(block = (malloc)(size + HEADER_SIZE))) { +#endif + if (!blockbytag[PU_CACHE]) + I_Error ("Z_Malloc: Failure trying to allocate %lu bytes" +#ifdef INSTRUMENTED + "\nSource: %s:%d" +#endif + ,(unsigned long) size +#ifdef INSTRUMENTED + , file, line +#endif + ); + Z_FreeTags(PU_CACHE,PU_CACHE); + } + + if (!blockbytag[tag]) + { + blockbytag[tag] = block; + block->next = block->prev = block; + } + else + { + blockbytag[tag]->prev->next = block; + block->prev = blockbytag[tag]->prev; + block->next = blockbytag[tag]; + blockbytag[tag]->prev = block; + } + + block->size = size; + +#ifdef INSTRUMENTED + if (tag >= PU_PURGELEVEL) + purgable_memory += block->size; + else + active_memory += block->size; +#endif + free_memory -= block->size; + +#ifdef INSTRUMENTED + block->file = file; + block->line = line; +#endif + +#ifdef ZONEIDCHECK + block->id = ZONEID; // signature required in block header +#endif + block->tag = tag; // tag + block->user = user; // user + block = (memblock_t *)((char *) block + HEADER_SIZE); + if (user) // if there is a user + *user = block; // set user to point to new block + +#ifdef INSTRUMENTED + Z_DrawStats(); // print memory allocation stats + // scramble memory -- weed out any bugs + memset(block, gametic & 0xff, size); +#endif + + return block; +} + +void (Z_Free)(void *p +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + memblock_t *block = (memblock_t *)((char *) p - HEADER_SIZE); + +#ifdef INSTRUMENTED +#ifdef CHECKHEAP + Z_CheckHeap(); +#endif + file_history[free_history][history_index[free_history]] = file; + line_history[free_history][history_index[free_history]++] = line; + history_index[free_history] &= ZONE_HISTORY-1; +#endif + + if (!p) + return; + + +#ifdef ZONEIDCHECK + if (block->id != ZONEID) + I_Error("Z_Free: freed a pointer without ZONEID" +#ifdef INSTRUMENTED + "\nSource: %s:%d" + "\nSource of malloc: %s:%d" + , file, line, block->file, block->line +#endif + ); + block->id = 0; // Nullify id so another free fails +#endif + + if (block->user) // Nullify user if one exists + *block->user = NULL; + + if (block == block->next) + blockbytag[block->tag] = NULL; + else + if (blockbytag[block->tag] == block) + blockbytag[block->tag] = block->next; + block->prev->next = block->next; + block->next->prev = block->prev; + + free_memory += block->size; +#ifdef INSTRUMENTED + if (block->tag >= PU_PURGELEVEL) + purgable_memory -= block->size; + else + active_memory -= block->size; + + /* scramble memory -- weed out any bugs */ + memset(block, gametic & 0xff, block->size + HEADER_SIZE); +#endif + +#ifdef HAVE_LIBDMALLOC + dmalloc_free(file,line,block,DMALLOC_FUNC_MALLOC); +#else + (free)(block); +#endif +#ifdef INSTRUMENTED + Z_DrawStats(); // print memory allocation stats +#endif +} + +void (Z_FreeTags)(int lowtag, int hightag +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ +#ifdef HEAPDUMP + Z_DumpMemory(); +#endif + + if (lowtag <= PU_FREE) + lowtag = PU_FREE+1; + + if (hightag > PU_CACHE) + hightag = PU_CACHE; + + for (;lowtag <= hightag; lowtag++) + { + memblock_t *block, *end_block; + block = blockbytag[lowtag]; + if (!block) + continue; + end_block = block->prev; + while (1) + { + memblock_t *next = block->next; +#ifdef INSTRUMENTED + (Z_Free)((char *) block + HEADER_SIZE, file, line); +#else + (Z_Free)((char *) block + HEADER_SIZE); +#endif + if (block == end_block) + break; + block = next; // Advance to next block + } + } +} + +void (Z_ChangeTag)(void *ptr, int tag +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + memblock_t *block = (memblock_t *)((char *) ptr - HEADER_SIZE); + + // proff - added sanity check, this can happen when an empty lump is locked + if (!ptr) + return; + + // proff - do nothing if tag doesn't differ + if (tag == block->tag) + return; + +#ifdef INSTRUMENTED +#ifdef CHECKHEAP + Z_CheckHeap(); +#endif +#endif + +#ifdef ZONEIDCHECK + if (block->id != ZONEID) + I_Error ("Z_ChangeTag: freed a pointer without ZONEID" +#ifdef INSTRUMENTED + "\nSource: %s:%d" + "\nSource of malloc: %s:%d" + , file, line, block->file, block->line +#endif + ); + + if (tag >= PU_PURGELEVEL && !block->user) + I_Error ("Z_ChangeTag: an owner is required for purgable blocks\n" +#ifdef INSTRUMENTED + "Source: %s:%d" + "\nSource of malloc: %s:%d" + , file, line, block->file, block->line +#endif + ); + +#endif // ZONEIDCHECK + + if (block == block->next) + blockbytag[block->tag] = NULL; + else + if (blockbytag[block->tag] == block) + blockbytag[block->tag] = block->next; + block->prev->next = block->next; + block->next->prev = block->prev; + + if (!blockbytag[tag]) + { + blockbytag[tag] = block; + block->next = block->prev = block; + } + else + { + blockbytag[tag]->prev->next = block; + block->prev = blockbytag[tag]->prev; + block->next = blockbytag[tag]; + blockbytag[tag]->prev = block; + } + +#ifdef INSTRUMENTED + if (block->tag < PU_PURGELEVEL && tag >= PU_PURGELEVEL) + { + active_memory -= block->size; + purgable_memory += block->size; + } + else + if (block->tag >= PU_PURGELEVEL && tag < PU_PURGELEVEL) + { + active_memory += block->size; + purgable_memory -= block->size; + } +#endif + + block->tag = tag; +} + +void *(Z_Realloc)(void *ptr, size_t n, int tag, void **user +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + void *p = (Z_Malloc)(n, tag, user DA(file, line)); + if (ptr) + { + memblock_t *block = (memblock_t *)((char *) ptr - HEADER_SIZE); + memcpy(p, ptr, n <= block->size ? n : block->size); + (Z_Free)(ptr DA(file, line)); + if (user) // in case Z_Free nullified same user + *user=p; + } + return p; +} + +void *(Z_Calloc)(size_t n1, size_t n2, int tag, void **user +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + return + (n1*=n2) ? memset((Z_Malloc)(n1, tag, user DA(file, line)), 0, n1) : NULL; +} + +char *(Z_Strdup)(const char *s, int tag, void **user +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + return strcpy((Z_Malloc)(strlen(s)+1, tag, user DA(file, line)), s); +} + +void (Z_CheckHeap)( +#ifdef INSTRUMENTED + const char *file, int line +#else + void +#endif + ) +{ +#if 0 + memblock_t *block; // Start at base of zone mem + if (block) + do { // Consistency check (last node treated special) + if ((block->next != zone && + (memblock_t *)((char *) block+HEADER_SIZE+block->size) != block->next) + || block->next->prev != block || block->prev->next != block) + I_Error("Z_CheckHeap: Block size does not touch the next block\n" +#ifdef INSTRUMENTED + "Source: %s:%d" + "\nSource of offending block: %s:%d" + , file, line, block->file, block->line +#endif + ); +//#ifdef INSTRUMENTED +// shouldn't be needed anymore, was just for testing +#if 0 + if (((int)block->file < 0x00001000) && (block->file != NULL) && (block->tag != 0)) { + block->file = NULL; + } +#endif + } while ((block=block->next) != zone); +#endif +} diff --git a/src/z_zone.h b/src/z_zone.h new file mode 100644 index 0000000..f70ce08 --- /dev/null +++ b/src/z_zone.h @@ -0,0 +1,129 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * 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. + * + * Rewritten by Lee Killough, though, since it was not efficient enough. + * + *---------------------------------------------------------------------*/ + +#ifndef __Z_ZONE__ +#define __Z_ZONE__ + +#ifndef __GNUC__ +#define __attribute__(x) +#endif + +// Include system definitions so that prototypes become +// active before macro replacements below are in effect. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include + +// ZONE MEMORY +// PU - purge tags. + +enum {PU_FREE, PU_STATIC, PU_SOUND, PU_MUSIC, PU_LEVEL, PU_LEVSPEC, PU_CACHE, + /* Must always be last -- killough */ PU_MAX}; + +#define PU_PURGELEVEL PU_CACHE /* First purgable tag's level */ + +#ifdef INSTRUMENTED +#define DA(x,y) ,x,y +#define DAC(x,y) x,y +#else +#define DA(x,y) +#define DAC(x,y) +#endif + +void *(Z_Malloc)(size_t size, int tag, void **ptr DA(const char *, int)); +void (Z_Free)(void *ptr DA(const char *, int)); +void (Z_FreeTags)(int lowtag, int hightag DA(const char *, int)); +void (Z_ChangeTag)(void *ptr, int tag DA(const char *, int)); +void (Z_Init)(void); +void Z_Close(void); +void *(Z_Calloc)(size_t n, size_t n2, int tag, void **user DA(const char *, int)); +void *(Z_Realloc)(void *p, size_t n, int tag, void **user DA(const char *, int)); +char *(Z_Strdup)(const char *s, int tag, void **user DA(const char *, int)); +void (Z_CheckHeap)(DAC(const char *,int)); // killough 3/22/98: add file/line info +void Z_DumpHistory(char *); + +#ifdef INSTRUMENTED +/* cph - save space if not debugging, don't require file + * and line to memory calls */ +#define Z_Free(a) (Z_Free) (a, __FILE__,__LINE__) +#define Z_FreeTags(a,b) (Z_FreeTags) (a,b, __FILE__,__LINE__) +#define Z_ChangeTag(a,b) (Z_ChangeTag)(a,b, __FILE__,__LINE__) +#define Z_Malloc(a,b,c) (Z_Malloc) (a,b,c, __FILE__,__LINE__) +#define Z_Strdup(a,b,c) (Z_Strdup) (a,b,c, __FILE__,__LINE__) +#define Z_Calloc(a,b,c,d) (Z_Calloc) (a,b,c,d,__FILE__,__LINE__) +#define Z_Realloc(a,b,c,d) (Z_Realloc) (a,b,c,d,__FILE__,__LINE__) +#define Z_CheckHeap() (Z_CheckHeap)(__FILE__,__LINE__) +#endif + +/* cphipps 2001/11/18 - + * If we're using memory mapped file access to WADs, we won't need to maintain + * our own heap. So we *could* let "normal" malloc users use the libc malloc + * directly, for efficiency. Except we do need a wrapper to handle out of memory + * errors... damn, ok, we'll leave it for now. + */ +#ifndef HAVE_LIBDMALLOC +// Remove all definitions before including system definitions + +#undef malloc +#undef free +#undef realloc +#undef calloc +#undef strdup + +#define malloc(n) Z_Malloc(n,PU_STATIC,0) +#define free(p) Z_Free(p) +#define realloc(p,n) Z_Realloc(p,n,PU_STATIC,0) +#define calloc(n1,n2) Z_Calloc(n1,n2,PU_STATIC,0) +#define strdup(s) Z_Strdup(s,PU_STATIC,0) + +#else + +#ifdef HAVE_LIBDMALLOC +#include +#endif + +#endif + +void Z_ZoneHistory(char *); + +#endif diff --git a/tests/boxes.pl b/tests/boxes.pl new file mode 100755 index 0000000..5da071f --- /dev/null +++ b/tests/boxes.pl @@ -0,0 +1,61 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my $SQUARES = 100; +my $WAD = "boxes.wad"; +my @LMPS = ("MAP01", "THINGS", "LINEDEFS", "SIDEDEFS", "VERTEXES", + "SEGS", "SSECTORS", "NODES", "SECTORS", "REJECT", "BLOCKMAP"); + +my %lmp; + +for (my ($s,$i) = (0,0); $i < $SQUARES; $i++) { + for (my $j = 0; $j < $SQUARES; $j++) { + rect($s++, 16*$i, 16*$j, 16*$i+8, 16*$j+8); + } +} + +$lmp{"THINGS"} = pack("S5", 4, 4, 0, 1, 7); + +open(F, ">$WAD"); +my $ptr = 12 + 16*scalar @LMPS; +print F pack("a4L2", "PWAD", scalar @LMPS, 12); +for (@LMPS) { + if (exists $lmp{$_}) { + print F pack("L2a8", $ptr, length $lmp{$_}, $_); + $ptr += length $lmp{$_}; + } else { + print F pack("L2a8", $ptr, 0, $_); + } +} +for (@LMPS) { print F $lmp{$_} if exists $lmp{$_}; } +close F; + +sub rect +{ + my ($n, $x0, $y0, $x1, $y1) = @_; + + $lmp{"SECTORS"}.=pack("S2a8a8S3", + 0, 128, "FLOOR4_8", "CEIL3_5", 192, 0, 0); + + $lmp{"VERTEXES"}.=pack("S2"x4, + $x0, $y0, + $x0, $y1, + $x1, $y1, + $x1, $y0); + + $lmp{"LINEDEFS"}.=pack("S7"x4, + 4*$n, 4*$n+1, 1, 0, 0, 4*$n, 65535, + 4*$n+1, 4*$n+2, 1, 0, 0, 4*$n+1, 65535, + 4*$n+2, 4*$n+3, 1, 0, 0, 4*$n+2, 65535, + 4*$n+3, 4*$n, 1, 0, 0, 4*$n+3, 65535); + + $lmp{"SIDEDEFS"}.=pack("S2a8a8a8S"x4, + 0, 0, "-", "-", "STARTAN3", $n, + 0, 0, "-", "-", "STARTAN3", $n, + 0, 0, "-", "-", "STARTAN3", $n, + 0, 0, "-", "-", "STARTAN3", $n); +} + +__END__ diff --git a/tests/crosses.pl b/tests/crosses.pl new file mode 100755 index 0000000..825f62c --- /dev/null +++ b/tests/crosses.pl @@ -0,0 +1,76 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my $grid = shift; +$grid = 75 if (!defined($grid)); + +my $WAD = "crosses.wad"; +my @LMPS = ("MAP01", "THINGS", "LINEDEFS", "SIDEDEFS", "VERTEXES", + "SEGS", "SSECTORS", "NODES", "SECTORS", "REJECT", "BLOCKMAP"); + +my %lmp; + +for (my ($s,$i) = (0,0); $i < $grid; $i++) { + for (my $j = 0; $j < $grid; $j++) { + rect($s++, 80*$i, 80*$j, 80*$i+64, 80*$j+64); + } +} + +$lmp{"THINGS"} = pack("S5", 32, 32, 0, 1, 7); + +open(F, ">$WAD"); +my $ptr = 12 + 16*scalar @LMPS; +print F pack("a4L2", "PWAD", scalar @LMPS, 12); +for (@LMPS) { + if (exists $lmp{$_}) { + print F pack("L2a8", $ptr, length $lmp{$_}, $_); + $ptr += length $lmp{$_}; + } else { + print F pack("L2a8", $ptr, 0, $_); + } +} +for (@LMPS) { print F $lmp{$_} if exists $lmp{$_}; } +close F; + +sub rect +{ + my ($n, $x0, $y0, $x1, $y1) = @_; + + $lmp{"SECTORS"}.=pack("S2a8a8S3", + 0, 128, "FLOOR4_8", "CEIL3_5", 192, 0, 0); + + $lmp{"VERTEXES"}.=pack("S2"x5, + $x0, $y0, + $x0, $y1, + $x1, $y1, + $x1, $y0, + ($x0+$x1)/2, ($y0+$y1)/2); + + $lmp{"LINEDEFS"}.=pack("S7"x8, + 5*$n, 5*$n+1, 1, 0, 0, 12*$n, 65535, + 5*$n+1, 5*$n+2, 1, 0, 0, 12*$n+1, 65535, + 5*$n+2, 5*$n+3, 1, 0, 0, 12*$n+2, 65535, + 5*$n+3, 5*$n, 1, 0, 0, 12*$n+3, 65535, + 5*$n, 5*$n+4, 4, 0, 0, 12*$n+4, 12*$n+5, + 5*$n+1, 5*$n+4, 4, 0, 0, 12*$n+6, 12*$n+7, + 5*$n+2, 5*$n+4, 4, 0, 0, 12*$n+8, 12*$n+9, + 5*$n+3, 5*$n+4, 4, 0, 0, 12*$n+10,12*$n+11); + + $lmp{"SIDEDEFS"}.=pack("S2a8a8a8S"x12, + 0, 0, "-", "-", "STARTAN3", $n, + 0, 0, "-", "-", "STARTAN3", $n, + 0, 0, "-", "-", "STARTAN3", $n, + 0, 0, "-", "-", "STARTAN3", $n, + 0, 0, "-", "-", "-", $n, + 0, 0, "-", "-", "-", $n, + 0, 0, "-", "-", "-", $n, + 0, 0, "-", "-", "-", $n, + 0, 0, "-", "-", "-", $n, + 0, 0, "-", "-", "-", $n, + 0, 0, "-", "-", "-", $n, + 0, 0, "-", "-", "-", $n); +} + +__END__ diff --git a/tests/demo-testing.csv b/tests/demo-testing.csv new file mode 100644 index 0000000..f50eb83 --- /dev/null +++ b/tests/demo-testing.csv @@ -0,0 +1,24 @@ +"Compat level tested","IWAD","PWAD","PWAD url","Demo","Demo URL","Player","Reason","Bug URL","Bug ref" +"ultdoom","doom.wad",,,"DEMO4",,,"Desyncs with most ports — the correct ending has the marine killed by the imp standing below him and doing a twisting dive into the acid",, +"finaldoom","plutonia.wad",,,"DEMO1",,,"Barrel stuck in ceiling in second courtyard causes desync with Boom/MBF","mbf-bugs.html","Compatibility bug in T_MovePlane" +"ultdoom","doom.wad",,,"n4m1-035.lmp","COMPETN/doom/nmare/n4m1-035.zip","Drew DeVore","telefrag p_map.c overlapping globals bug","mbf-bugs.html","Overlapping uses of global variables in p_map.c" +"finaldoom","plutonia.wad",,,"pl27-051.lmp","COMPETN/plutonia/speed/pl27-051.zip","Andre Budko","final doom teleport height emulation","mbf-bugs.html","Final Doom teleport dome desyncs" +"doom2_19","doom2.wad",,,"30cnvin.lmp","COMPETN/doom2/coop/movies/30cn3519.zip","Vincent Catalaa, Jim Leonard","intermission screen desync at end of MAP29 in prboom 2.2.4","mbf-bugs.html","Intermission screen secrets desync" +"doom2_19","doom2.wad",,,"30cn-xv.lmp","COMPETN/doom2/coop/movies/30cn3519.zip","Vincent Catalaa, Jim Leonard","intermission screen desync at end of MAP29 in prboom 2.2.4","mbf-bugs.html","Intermission screen secrets desync" +"doom2_19","doom2.wad",,,"lv19-509.lmp","COMPETN/doom2/max/lv19-509.zip","Radek Pecka","Prboom 2.3.0 switch bug",, +"doom2_19","doom2.wad","mm.wad","IDGAMES/themes/mm/mm_allup.zip","DEMO3",,,"Third demo failed with lxdoom 1.3.0 LOS optimisations",, +"doom2_19","doom2.wad","mm2.wad","IDGAMES/themes/mm/mm2.zip","DEMO1",,,"Failed for a while with lxdoom",, +"doom2_19","doom2.wad",,,"30ns6936.lmp","COMPETN/doom2/movie/30ns6936.zip","Henning Skogsto","MBF player bobbing bug","mbf-bugs.html","MBF player bobbing rewrite causes demo sync problems" +"doom2_19","doom2.wad","mm.wad","IDGAMES/themes/mm/mm_allup.zip","mm10-uv.lmp","IDGAMES/themes/mm/mmuvlmps.zip","Yonatan Donner","Stair building compat issues","mbf-bugs.html","Compatibility bug in EV_BuildStairs" +"doom2_19","doom2.wad","mm.wad","IDGAMES/themes/mm/mm_allup.zip","mm12-uv.lmp","IDGAMES/themes/mm/mmuvlmps.zip","Yonatan Donner","Stair building compat issues","mbf-bugs.html","Compatibility bug in EV_BuildStairs" +"doom2_19","doom2.wad","mm.wad","IDGAMES/themes/mm/mm_allup.zip","mm13-uv.lmp","IDGAMES/themes/mm/mmuvlmps.zip","Yonatan Donner","Stair building compat issues","mbf-bugs.html","Compatibility bug in EV_BuildStairs" +"doom2_19","doom2.wad","hr.wad","IDGAMES/themes/hr/hr.zip","hr06-uv.lmp","IDGAMES/themes/hr/hruvlmps.zip","Yonatan Donner","More LOS optimization bugs","mbf-bugs.html","Compatibility bug in P_CheckSight" +"doom2_19","doom2.wad","hr.wad","IDGAMES/themes/hr/hr.zip","hr18-348.lmp","COMPETN/pwads/hr/speed/hr18-348.zip","Chris Ratcliff","spechit overflows",, +"doom2_19","doom2.wad","hr.wad","IDGAMES/themes/hr/hr.zip","hr22-uv.lmp","IDGAMES/themes/hr/hruvlmp2.zip","Anders Johnsen","lost soul bouncing compatibility","mbf-bugs.html","Bouncing lost souls" +"doom2_19","doom2.wad","punisher.wad","IDGAMES/levels/doom2/p-r/punishr.zip","punisher.lmp","IDGAMES/levels/doom2/p-r/punishr.zip","Dario Casali","lost soul bouncing compatibility","mbf-bugs.html","Bouncing lost souls" +"doom2_19","doom2.wad","requiem.wad","IDGAMES/levels/doom2/megawads/requiem.zip","rq22-318.lmp","COMPETN/pwads/requiem/speed/rq22-318.zip","Adam Williamson","REJECT padding for buggy levels",, +"boom202","doom2.wad","sprike.wad","IDGAMES/levels/doom2/Ports/s-u/sprike.zip","sprike.lmp","IDGAMES/levels/doom2/Ports/s-u/sprike.zip","Simon Varseszeghy","Boom 2.02 test",, +"mbf","plutonia.wad",,,"plnm7104.lmp","SDA/tas/tas_id.zip","(TAS) Istvan Pataki","MBF test",, +"mbf","doom2.wad",,,"30uv1617.lmp","SDA/tas/tas_id.zip","(TAS) Marijo Sedlic","MBF test",, +"prboom22x","doom2.wad","hr.wad","IDGAMES/themes/hr/hr.zip","hrhmp.lmp","n/a","(TAS) me","Prboom 2.2.x with loads & saves test",, +"","doom.wad","uac_dead.wad","IDGAMES/levels/doom/s-u/uac_dead.zip","uac_dead.lmp","IDGAMES/lmps/doom/ultimate/uac_dead.zip","","",, diff --git a/tests/donut_av.wad b/tests/donut_av.wad new file mode 100644 index 0000000..7df3f5b Binary files /dev/null and b/tests/donut_av.wad differ diff --git a/tests/heightlist_overflow.wad b/tests/heightlist_overflow.wad new file mode 100644 index 0000000..4473130 Binary files /dev/null and b/tests/heightlist_overflow.wad differ diff --git a/tests/lmpwatch.pl b/tests/lmpwatch.pl new file mode 100755 index 0000000..50d097f --- /dev/null +++ b/tests/lmpwatch.pl @@ -0,0 +1,182 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Getopt::Std; $Getopt::Std::STANDARD_HELP_VERSION = 1; + +sub VERSION_MESSAGE +{ + print <<"EOF"; +lmpwatch.pl: a clone of PrBoom-Plus's automated demo playback system. +EOF +} + +sub HELP_MESSAGE +{ + print <<"EOF"; +Usage: $0 [OPTIONS] DEMO_FILES... + +Options: + -d x name of doom executable (try "echo" to test the program) + -e x any extra options to pass to doom + -p x name of patterns file from lmpwatch.zip + -s x colon- or space-separated search path, may contain wildcards + -t test demo sync (not fully implemented, needs engine-side support) + -v print lots of debugging output + +EOF +} + +getopts('d:e:p:s:tv', \my %opts) or do { + HELP_MESSAGE(); + die "$0: bad options\n" +}; +if (!@ARGV) { + HELP_MESSAGE(); + die "$0: no demos?\n"; +} + +my $verbose = defined($opts{'v'}); + +# name of doom executable +my $DOOM = (defined $opts{'d'}) ? $opts{'d'} : "prboom"; +# any extra options to pass? +my $extra = (defined $opts{'e'}) ? $opts{'e'} : ""; + +# are we playing back demos or testing them for demosync? +my $testing = defined($opts{'t'}); +# "batch mode" if you're testing a demo (turns off the renderer) +my $demo_opts = $testing ? "-nodraw -nosound -timedemo" : "-playdemo"; + +# paths to search for files, these will be subject to filename globbing +my @paths = do { + # expand colon- or space-separated string into list + my @s = defined($opts{'s'}) ? split(/[: ]+/, $opts{'s'}) : (); + + # expand paths that contain wildcards + map { glob $_ } grep { defined $_ } (@s, $ENV{"DOOMWADDIR"}, + "/usr/local/share/games/doom", "/usr/share/games/doom"); +}; +print "DIRS: @paths\n\n" if $verbose; + +# name of the patterns file from lmpwatch.zip +my $patsfile = do { + my $p = (defined $opts{'p'}) ? $opts{'p'} : "patterns.txt"; + path_expand($p, @paths); +} or die "$0: lmpwatch.zip patterns file is needed to use this program\n"; +print "PATTERNS: $patsfile\n\n" if $verbose; +open my ($PATTERNS), $patsfile; + +while (@ARGV) { + my $demo = path_expand(shift @ARGV, @paths) or next; + my ($comment, $iwad, @files) = parse_patterns($PATTERNS, $demo); + print "DEMO: $demo\nIWAD: $iwad\nFILES: @files\n" if $verbose; + + # filename globbing and switch prepending for wad files + $iwad = path_expand($iwad, @paths) or next; # not strictly necessary + @files = map { path_expand($_, @paths) or next; } @files; + my @deh = grep { /\.(deh|bex)$/i } @files; + unshift @deh, "-deh" if @deh; + my @wad = grep { /\.wad$/i } @files; + unshift @wad, "-file" if @wad; # should be "-merge" for chocolate-doom? + + my $command = sprintf("%s -iwad %s %s %s %s %s %s", + $DOOM, $iwad, "@wad", "@deh", $demo_opts, $demo, $extra); + print "RUNNING: $command\n" if $verbose; + + if ($testing) { + my @finished = test_demo($command); + # unfortunately there is not yet an easy way to test if the + # list of maps the player exited is correct. lmpwatch.zip is + # only meant for demo playback, not testing. + # just print out the names of maps that were completed + print "FINISHED: " if $verbose; + print join(" ", (split(m!/!, $demo))[-1], @finished), "\n"; + } else { + system($command); + } + print "\n" if $verbose; +} + +close $PATTERNS; + +exit 0; + +# -------------------------------------------------------------------------- + +# play back a demo, return list of maps the player finishes +# requires that the engine prints "FINISHED: " when a map is beaten +sub test_demo +{ + my ($command) = @_; + my @finished; + + # run the game and capture its output + open my ($doom), "$command 2>&1 |"; + # make a list of maps the player managed to exit + # this needs engine-side support + while (<$doom>) { + push @finished, $1 if m/^FINISHED: (.*)$/ + } + close $doom; + + return @finished; +} + +# parse lmpwatch.zip's patterns file to determine wads required to play a demo +sub parse_patterns +{ + my ($PATTERNS, $demofile) = @_; + my ($mask, $comment, $pattern, $iwad, @files); + + my ($demo) = (split m!/!,$demofile)[-1]; + + seek $PATTERNS, 0, 0; # rewind file to start + while (<$PATTERNS>) { + chomp; + # read valid key/value pairs + my ($key, $value) = m/^(\w+)\s+\"(.+)\"/; + next if !defined($key) || !defined($value); + + # set the pattern mask + $mask = $value, next if $key eq 'demo_patterns_mask'; + + # ignore keys that don't start with the pattern mask + next if !$mask || $key !~ m/^$mask/; + + # decode the pattern + ($comment, $pattern, my $files) = split(m!/!, $value); + # the first file is always the iwad, apparently + ($iwad, @files) = split(m!\|!, $files); + next if !$iwad; # no iwad?? + + # stop at the first match (conflict avoidance) + last if $demo =~ m/$pattern/ + } + + return ($comment, $iwad, @files); +} + +# from the list supplied, find the first directory a given filename is in +sub path_expand +{ + my $file = shift; + + # if it's already a valid filename, skip the searching + return $file if -f $file; + + print " SEARCHING: $file\n" if $verbose; + for my $dir (@_) { + my $path = "$dir/$file"; + print " TRYING: $path\n" if $verbose; + if (-f $path) { + print " FOUND: $file => $path\n" if $verbose; + return $path; + } + } + warn "$0: cannot find $file\n"; + return undef; +} + +# vim:set sts=2 sw=2 ts=8 et: diff --git a/tests/missed_back_side.zip b/tests/missed_back_side.zip new file mode 100644 index 0000000..7be4bbf Binary files /dev/null and b/tests/missed_back_side.zip differ diff --git a/tests/runtests.py b/tests/runtests.py new file mode 100755 index 0000000..6792f98 --- /dev/null +++ b/tests/runtests.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +import csv +import codecs +import os +import urllib2 +import shutil +import zipfile +import sys +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO +try: + import subprocess +except ImportError: + subprocess = None +from pprint import pprint + +url_lookup = { + 'COMPETN': 'ftp://competn.doom2.net:8002/pub/compet-n', + 'IDGAMES': 'http://www.gamers.org/pub/idgames', + 'SDA': 'ftp://competn.doom2.net:8002/pub/sda', +} + +basepath = None + +def iterDemoSpecs(specs): + #reader = csv.reader(codecs.open(specs,"r","utf-8")) + reader = csv.reader(open(specs,"r")) + headers = tuple(reader.next()) + for row in reader: + row = [x.strip() for x in row] + spec = dict(zip(headers, row)) + if len(spec): + yield spec + +def getFileFromZip(filename, filepath, package): + names = [x for x in package.namelist() if x.lower()==filename.lower()] + if len(names) == 0: + for name in package.namelist(): + if not name.lower().endswith('.zip'): + continue + data = StringIO(package.read(name)) + try: + subpackage = zipfile.ZipFile(data, 'r') + except zipfile.BadZipfile: + continue + if getFileFromZip(filename, filepath, subpackage): + return True + return False + dst = open(filepath, 'wb') + dst.write(package.read(names[0])) + dst.close() + package.close() + return True + +def getPathToFile(name, path): + if path == 'n/a': + if os.path.exists(os.path.abspath(name)): + return os.path.abspath(name) + return None + if path == '': + return '' + if path != '': + parts = path.split('/') + dirpath = os.path.abspath(os.path.join(*parts[:-1])) + filename = parts[-1] + filepath = os.path.abspath(os.path.join(dirpath, name)) + if os.path.exists(filepath): + return filepath + if not os.path.exists(path): + if not os.path.exists(dirpath): + os.makedirs(dirpath) + parts[0] = url_lookup.get(parts[0], parts[0]) + url = '/'.join(parts) + if not url.startswith('http://') \ + and not url.startswith('ftp://'): + raise ValueError, "unknown url '%s'" % url + print "Fetching %s" % url + try: + request = urllib2.urlopen(url) + except urllib2.HTTPError: + print "Error retriving the file." + return None + dst = open(path, 'wb') + shutil.copyfileobj(request, dst) + dst.close() + request.close() + if filename != name: + if filename.lower().endswith('.zip'): + package = zipfile.ZipFile(path, 'r') + if not getFileFromZip(name, filepath, package): + return None + return filepath + +def runtest(iwad, demo, demopath, pwad): + executable = '' + iwadpath = '' + use_pipe = False + if sys.platform == 'win32': + os.chdir(os.path.join(basepath, 'release')) + executable = 'prboom' + iwadpath = iwad + use_pipe = False + elif sys.platform == 'darwin': + os.chdir(os.path.join(basepath, 'src')) + executable = 'PrBoom.app/Contents/MacOS/PrBoom' + iwadpath = iwad + use_pipe = True + + options = [ + executable, + '-nodraw', + '-nosound', + '-nomouse', + '-nofullscreen', + '-width', '320', + '-height', '200', + ] + options.extend(('-iwad', iwadpath)) + if demopath is not None and demopath != '': + options.extend(('-fastdemo', demopath)) + else: + options.extend(('-fastdemo', demo)) + if pwad is not None and pwad != '': + options.extend(('-file', pwad)) + cmd = ' '.join(options) + print cmd + if sys.platform == 'win32': + p = subprocess.call(options) + try: + results = open(os.path.join(basepath, 'release', 'stderr.txt'),'rU').readlines() + results = [x.strip() for x in results if x.strip()] + if len(results): + print results[-1] + except IOError: + print "couldn't open stderr.txt" + else: + p = subprocess.Popen(options, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (stdout, stderr) = p.communicate() + results = stderr.split('\n') + results = [x.strip() for x in results if x.strip()] + if len(results): + print results[-1] + +def getBasePath(): + curpath = os.path.join(os.getcwd(), __file__) + if not os.path.exists(curpath): + return + while 1: + if os.path.isfile(curpath): + curpath = os.path.split(curpath)[0] + if os.path.isdir(curpath): + contents = os.listdir(curpath) + if 'prboom.spec.in' in contents: + return curpath + oldpath = curpath + curpath = os.path.split(curpath)[0] + if oldpath == curpath: + return + +def run(): + global basepath + basepath = getBasePath() + if basepath is None: + raise ValueError, "Can't determine base path" + for demospec in iterDemoSpecs(os.path.join(basepath, 'tests', 'demo-testing.csv')): + os.chdir(os.path.join(basepath, 'tests')) + demoname, demopath = demospec['Demo'], demospec['Demo URL'] + demopath = getPathToFile(demoname, demopath) + pwadname, pwadpath = demospec['PWAD'], demospec['PWAD url'] + pwadpath = getPathToFile(pwadname, pwadpath) + iwad = demospec['IWAD'] + runtest(iwad, demoname, demopath, pwadpath) + +if __name__=='__main__': + run() diff --git a/tests/sky.wad b/tests/sky.wad new file mode 100644 index 0000000..d481486 Binary files /dev/null and b/tests/sky.wad differ diff --git a/tests/weaponinfo.wad b/tests/weaponinfo.wad new file mode 100644 index 0000000..bccffb3 Binary files /dev/null and b/tests/weaponinfo.wad differ